<?php

namespace api\classes;

use api\classes\API;
use JsonException;

require_once 'API.php';

class API_servers extends API
{
    public function getServers($returnBoolean = false)
    {

        list($query, $types, $params) = $this->buildDynamicQuery('servers');

        $items = $this->generalGetFunction($query, $types, $params, $returnBoolean, 'Server');

        return $items;
    }

    public function validateDiskData($disks)
    {
        foreach ($disks as $index => $disk) {

            // Ensure $disk is an array
            if (!is_array($disk)) {
                $this->apiOutput(400, ['error' => "Disk entry is not an array"]);
            }

            $requiredFields = ['disk_name', 'disk_space', 'disk_used', 'disk_location'];

            foreach ($requiredFields as $field) {
                if (!array_key_exists($field, $disk)) {
                    $this->apiOutput(400, ['error' => "Missing required field '$field' in disk information"]);
                }
                switch ($field) {
                    case 'disk_used':
                    case 'disk_space':
                        $disks[$index][$field] = $this->validateSingleData($disk[$field], ['type' => 'float']);
                        break;
                    case 'disk_location':
                    case 'disk_name':
                        $disks[$index][$field] = $this->validateSingleData($disk[$field], ['type' => 'string']);
                        break;
                }
            }
        }

        try {
            return json_encode($disks, JSON_THROW_ON_ERROR);
        } catch (JsonException $e) {
            $this->apiOutput(400, ['error' => "Failed to encode disk data to JSON: " . $e->getMessage()]);
        }
    }


    public function updateServer()
    {

        # if the server_state that is posted is 'deleted' check if the current server state is 'new' if so, delete it perm.
        if ($this->data['server_state'] == 'deleted') {
            $stmt = $GLOBALS['pdo']->prepare("SELECT server_state FROM servers WHERE server_vm_id = ? AND server_state = 'new'");
            $stmt->execute([$this->data['server_vm_id']]);

            if ($stmt->fetch()) {
                $deleteStmt = $GLOBALS['pdo']->prepare("DELETE FROM servers WHERE server_vm_id = ? AND server_state = 'new'");
                $deleteStmt->execute([$this->data['server_vm_id']]);
                return;
            }
        }

        if (isset($this->data['company_uuid'])) {
            if (strlen($this->data['company_uuid']) == 0) {
                $this->data['company_uuid'] = NULL;
            }
        }

        $fields = [
            'company_uuid',
            'server_vm_id',
            'server_vm_host_id',
            'server_vm_host_name',
            'server_power_state',
            'server_state',
            'server_hostname',
            'server_os',
            'server_cpu',
            'server_memory',
            'server_memory_demand',
            'server_disks',
            'server_ipv4',
            'server_ipv6',
            'server_vm_generation',
            'server_vm_snapshot',
            'server_licenses',
            'server_backup',
            'server_description'
        ];

        $insertFields = ['server_uuid'];
        $insertValues = ['UUID()'];
        $bindParams = [];

        foreach ($fields as $field) {
            if (array_key_exists($field, $this->data)) {
                $insertFields[] = $field;
                $insertValues[] = ":$field";
                $bindParams[":$field"] = $this->data[$field];
            }
        }

        # Always include server_create_timestamp and server_modified_timestamp
        $insertFields[] = 'server_create_timestamp';
        $insertValues[] = ':server_create_timestamp';
        $bindParams[':server_create_timestamp'] = time();

        $insertFields[] = 'server_modified_timestamp';
        $insertValues[] = ':server_modified_timestamp';
        $bindParams[':server_modified_timestamp'] = time();

        $query = "INSERT INTO servers (" . implode(',', $insertFields) . ")
                    VALUES (" . implode(',', $insertValues) . ")
                    ON DUPLICATE KEY UPDATE ";

        # Build the ON DUPLICATE KEY UPDATE, only for fields that exist
        $updateParts = [];
        foreach ($insertFields as $field) {
            if (!in_array($field, ['server_create_timestamp', 'server_uuid'])) {
                $updateParts[] = "$field = VALUES($field)";
            }
        }
        $query .= implode(", ", $updateParts);

        $stmt = $GLOBALS['pdo']->prepare($query);

        if (!$stmt->execute($bindParams)) {
            $this->apiOutput(400, ['error' => "Failed to insert server into database"]);
        }
    }

    private function validateLicenseData($server_vm_id, $server_licenses)
    {
        $server_vm_id = $this->validateSingleData($server_vm_id, ['type' => 'string']);
        $server_licenses_posted = $this->validateSingleData($server_licenses, ['type' => 'array']);

        $query = "SELECT server_licenses FROM servers WHERE server_vm_id = ?";
        $stmt = $this->prepareStatement($query);
        $stmt->bind_param("s", $server_vm_id);
        $this->executeStatement($stmt);
        $result = $stmt->get_result();

        $server_licenses_db = $result->fetch_assoc();

        $server_licenses_db = $server_licenses_db['server_licenses'] ?? null;
        $server_licenses_db_new = [];
        if (!empty($server_licenses_db)) {
            $decoded = json_decode($server_licenses_db, true);
            if (is_array($decoded)) {
                foreach ($decoded as $item) {
                    foreach ($item as $key => $value) {
                        $server_licenses_db_new[$key] = $value;
                    }
                }
            }
        }

        foreach ($server_licenses_posted as $item) {
            foreach ($item as $rawKey => $value) {

                $prefix = substr($rawKey, 0, 1);
                $license = substr($rawKey, 1);

                if ($prefix === '+') {
                    $server_licenses_db_new[$license] = $value;
                }

                if ($prefix === '-') {
                    unset($server_licenses_db_new[$license]);
                }
            }
        }

        $server_licenses_db_new_final = [];
        foreach ($server_licenses_db_new as $key => $value) {
            $server_licenses_db_new_final[] = [$key => $value];
        }

        return empty($server_licenses_db_new_final) ? '[]' : json_encode($server_licenses_db_new_final);
    }

    private function validateBackupData($server_vm_id, $server_backup)
    {
        $server_vm_id = $this->validateSingleData($server_vm_id, ['type' => 'string']);
        $server_backup_posted = $this->validateSingleData($server_backup, ['type' => 'array']);

        $query = "SELECT server_backup FROM servers WHERE server_vm_id = ?";
        $stmt = $this->prepareStatement($query);
        $stmt->bind_param("s", $server_vm_id);
        $this->executeStatement($stmt);
        $result = $stmt->get_result();

        $server_backup_db = $result->fetch_assoc();

        $server_backup_db = $server_backup_db['server_backup'] ?? null;
        $server_backup_db_new = [];
        if (!empty($server_backup_db)) {
            $decoded = json_decode($server_backup_db, true);
            if (is_array($decoded)) {
                foreach ($decoded as $item) {
                    foreach ($item as $key => $value) {
                        $server_backup_db_new[$key] = $value;
                    }
                }
            }
        }

        foreach ($server_backup_posted as $item) {
            foreach ($item as $rawKey => $value) {

                $prefix = substr($rawKey, 0, 1);
                $backup = substr($rawKey, 1);

                if ($prefix === '+') {
                    $server_backup_db_new[$backup] = $value;
                }

                if ($prefix === '-') {
                    unset($server_backup_db_new[$backup]);
                }
            }
        }

        $server_backup_db_new_final = [];
        foreach ($server_backup_db_new as $key => $value) {
            $server_backup_db_new_final[] = [$key => $value];
        }

        return empty($server_backup_db_new_final) ? '[]' : json_encode($server_backup_db_new_final);
    }

    public function processServerData($server, $requiredFields, $optionalFields)
    {
        // since the disk data is sent as an array we need to check it separately from the other data validations

        if (!empty($server['server_disks']) && is_array($server['server_disks'])) {
            $server['server_disks'] = $this->validateDiskData($server['server_disks']);
        } else {
            unset($server['server_disks']);
        }

        if (!empty($server['server_licenses']) && is_array($server['server_licenses'])) {
            $server['server_licenses'] = $this->validateLicenseData($server['server_vm_id'], $server['server_licenses']);
        } else {
            unset($server['server_licenses']);
        }

        if (!empty($server['server_backup']) && is_array($server['server_backup'])) {
            $server['server_backup'] = $this->validateBackupData($server['server_vm_id'], $server['server_backup']);
        } else {
            unset($server['server_backup']);
        }

        foreach (['server_ipv4', 'server_ipv6'] as $key) {
            if (!empty($server[$key]) && is_array($server[$key])) {
                $server[$key] = json_encode($server[$key]);
            } else {
                unset($server[$key]);
            }
        }

        $this->postedData = $server;

        $this->validateData($requiredFields, $optionalFields);

        $this->updateServer();
    }
}