<?php

namespace App\Services\Proxmox;

use App\Models\Host;

class FakeProxmoxClient implements ProxmoxClient
{
    public ?Host $host = null;

    /** @var array<int, array<string, mixed>> */
    public array $nodes = [['node' => 'pve']];

    /** @var array<string, mixed> */
    public array $status = [
        'cpuinfo' => ['cpus' => 16],
        'memory' => ['total' => 68719476736], // 64 GiB
        'pveversion' => 'pve-manager/8.2.2',
    ];

    /** @var array<int, array<string, mixed>> */
    public array $storage = [
        ['storage' => 'local', 'type' => 'dir', 'content' => 'iso,vztmpl,backup', 'total' => 1099511627776],
        ['storage' => 'local-lvm', 'type' => 'lvmthin', 'content' => 'images,rootdir', 'total' => 1099511627776], // 1 TiB
    ];

    public function forHost(Host $host): static
    {
        $this->host = $host;

        return $this;
    }

    public function listNodes(): array
    {
        return $this->nodes;
    }

    public function nodeStatus(string $node): array
    {
        return $this->status;
    }

    public function nodeStorage(string $node): array
    {
        return $this->storage;
    }

    // --- VM lifecycle (deterministic; configurable failure hooks for tests) ---

    public int $vmidCounter = 100;

    /** @var array<int, int> */
    public array $clonedVmids = [];

    /** @var array<int, int> */
    public array $runningVmids = [];

    /** @var array<int, string> */
    public array $cloudInitCalls = [];

    /** @var array<int, array<string, mixed>> vmid => params */
    public array $cloudInitParams = [];

    /** @var array<int, string> */
    public array $resizeCalls = [];

    /** @var array<int, string> */
    public array $firewallCalls = [];

    /** @var array<int, string> recorded guest commands */
    public array $guestCommands = [];

    /** @var array<string, array{exitcode:int,out-data:string}> */
    private array $guestScripts = [];

    public int $guestDefaultExit = 0;

    public string $guestDefaultOut = '';

    public bool $guestAgentUp = true;

    public ?string $forceTaskStatus = null; // set to 'running' to force a poll

    public string $taskExitStatus = 'OK';   // set to a non-OK value to fail a task

    public function nextVmid(): int
    {
        return $this->vmidCounter++;
    }

    public function cloneVm(string $node, int $templateVmid, int $newVmid, string $name): string
    {
        $this->clonedVmids[] = $newVmid;

        return 'UPID:pve:qmclone:'.$newVmid;
    }

    public function setCloudInit(string $node, int $vmid, array $params): void
    {
        $this->cloudInitCalls[] = $vmid;
        $this->cloudInitParams[$vmid] = $params;
    }

    public function resizeDisk(string $node, int $vmid, string $disk, string $size): void
    {
        $this->resizeCalls[] = $vmid.':'.$disk.':'.$size;
    }

    public function startVm(string $node, int $vmid): string
    {
        $this->runningVmids[] = $vmid;

        return 'UPID:pve:qmstart:'.$vmid;
    }

    /** Set to e.g. 'clone' to simulate a VM still locked by a running clone task. */
    public ?string $vmLock = null;

    /** Cumulative counters per vmid, as Proxmox reports them: [netin, netout]. */
    public array $counters = [];

    /** vmid => MB/s currently configured, or null when unlimited. */
    public array $networkRates = [];

    public function vmStatus(string $node, int $vmid): array
    {
        return [
            'status' => in_array($vmid, $this->runningVmids, true) ? 'running' : 'stopped',
            'lock' => $this->vmLock,
            'netin' => $this->counters[$vmid]['netin'] ?? 0,
            'netout' => $this->counters[$vmid]['netout'] ?? 0,
        ];
    }

    public function setNetworkRate(string $node, int $vmid, ?float $mbytesPerSecond): void
    {
        $this->networkRates[$vmid] = $mbytesPerSecond;
    }

    public function vmExists(string $node, int $vmid): bool
    {
        return in_array($vmid, $this->clonedVmids, true) || in_array($vmid, $this->runningVmids, true);
    }

    /** @var array<int, int> */
    public array $deletedVmids = [];

    public function deleteVm(string $node, int $vmid): void
    {
        $this->deletedVmids[] = $vmid;
        $this->clonedVmids = array_values(array_diff($this->clonedVmids, [$vmid]));
        $this->runningVmids = array_values(array_diff($this->runningVmids, [$vmid]));
    }

    public function guestAgentPing(string $node, int $vmid): bool
    {
        return $this->guestAgentUp;
    }

    public function guestScript(string $substring, int $exitcode, string $out = ''): static
    {
        $this->guestScripts[$substring] = ['exitcode' => $exitcode, 'out-data' => $out];

        return $this;
    }

    public function guestExec(string $node, int $vmid, string $command): array
    {
        $this->guestCommands[] = $command;

        foreach ($this->guestScripts as $substring => $result) {
            if (str_contains($command, $substring)) {
                return $result;
            }
        }

        return ['exitcode' => $this->guestDefaultExit, 'out-data' => $this->guestDefaultOut];
    }

    public function guestRan(string $substring): bool
    {
        foreach ($this->guestCommands as $command) {
            if (str_contains($command, $substring)) {
                return true;
            }
        }

        return false;
    }

    public function taskStatus(string $node, string $upid): array
    {
        return ['status' => $this->forceTaskStatus ?? 'stopped', 'exitstatus' => $this->taskExitStatus];
    }

    public function applyFirewall(string $node, int $vmid, array $rules): void
    {
        $this->firewallCalls[] = (string) $vmid;
    }

    /** @var array<int, string> */
    public array $backupJobs = [];

    public function createBackupJob(string $node, int $vmid, string $schedule): string
    {
        $id = 'backup-'.$vmid;
        $this->backupJobs[] = $id;

        return $id;
    }
}
