<?php

declare(strict_types=1);

namespace WhmcsMigrate;

final class StateManager
{
    private string $stateDir;

    /** @var array<string, array<string|int, int>> Entity type => [whmcsId => ezscaleId] */
    private array $mappings = [];

    /** @var array<string, array{status: string, offset: int, count: int, errors: int}> */
    private array $progress = [];

    public function __construct(string $stateDir)
    {
        $this->stateDir = $stateDir;

        if (! is_dir($stateDir)) {
            mkdir($stateDir, 0755, true);
        }

        $this->load();
    }

    /**
     * Get the EZSCALE ID mapped to a WHMCS ID for a given entity type.
     */
    public function getMapping(string $entity, int|string $whmcsId): ?int
    {
        $key = (string) $whmcsId;

        return $this->mappings[$entity][$key] ?? null;
    }

    /**
     * Store a WHMCS-to-EZSCALE ID mapping.
     */
    public function setMapping(string $entity, int|string $whmcsId, int $ezscaleId): void
    {
        $key = (string) $whmcsId;
        $this->mappings[$entity][$key] = $ezscaleId;
        $this->save();
    }

    /**
     * Get all mappings for a given entity type.
     *
     * @return array<string|int, int>
     */
    public function getAllMappings(string $entity): array
    {
        return $this->mappings[$entity] ?? [];
    }

    /**
     * Get the number of mappings for a given entity type.
     */
    public function getMappingCount(string $entity): int
    {
        return count($this->mappings[$entity] ?? []);
    }

    /**
     * Get the progress record for a phase.
     *
     * @return array{status: string, offset: int, count: int, errors: int}
     */
    public function getProgress(string $phase): array
    {
        return $this->progress[$phase] ?? [
            'status' => 'pending',
            'offset' => 0,
            'count' => 0,
            'errors' => 0,
        ];
    }

    /**
     * Set the progress record for a phase.
     */
    public function setProgress(string $phase, string $status, int $offset = 0, int $count = 0, int $errors = 0): void
    {
        $this->progress[$phase] = [
            'status' => $status,
            'offset' => $offset,
            'count' => $count,
            'errors' => $errors,
        ];
        $this->save();
    }

    /**
     * Check if a phase has been marked complete.
     */
    public function isPhaseComplete(string $phase): bool
    {
        return ($this->progress[$phase]['status'] ?? 'pending') === 'complete';
    }

    /**
     * Delete all state files and reset in-memory state.
     */
    public function reset(): void
    {
        $this->mappings = [];
        $this->progress = [];

        $mappingsFile = $this->stateDir . '/id_mappings.json';
        $progressFile = $this->stateDir . '/progress.json';

        if (file_exists($mappingsFile)) {
            unlink($mappingsFile);
        }

        if (file_exists($progressFile)) {
            unlink($progressFile);
        }
    }

    /**
     * Persist current state to disk (JSON files).
     */
    public function save(): void
    {
        file_put_contents(
            $this->stateDir . '/id_mappings.json',
            json_encode($this->mappings, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE),
        );

        file_put_contents(
            $this->stateDir . '/progress.json',
            json_encode($this->progress, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE),
        );
    }

    /**
     * Load state from disk if files exist.
     */
    private function load(): void
    {
        $mappingsFile = $this->stateDir . '/id_mappings.json';
        if (file_exists($mappingsFile)) {
            $data = json_decode(file_get_contents($mappingsFile), true);
            if (is_array($data)) {
                $this->mappings = $data;
            }
        }

        $progressFile = $this->stateDir . '/progress.json';
        if (file_exists($progressFile)) {
            $data = json_decode(file_get_contents($progressFile), true);
            if (is_array($data)) {
                $this->progress = $data;
            }
        }
    }
}
