<?php

declare(strict_types=1);

namespace WhmcsMigrate;

use RuntimeException;
use Throwable;
use WhmcsMigrate\Phases\Phase1Clients;
use WhmcsMigrate\Phases\Phase2Products;
use WhmcsMigrate\Phases\Phase3Services;
use WhmcsMigrate\Phases\Phase4Invoices;
use WhmcsMigrate\Phases\Phase5Transactions;
use WhmcsMigrate\Phases\Phase6Coupons;
use WhmcsMigrate\Phases\Phase7Orders;
use WhmcsMigrate\Phases\PhaseInterface;

final class MigrationRunner
{
    private Config $config;

    private Logger $logger;

    private Database $db;

    private WhmcsApi $api;

    private StateManager $state;

    private Encryptor $encryptor;

    private Validator $validator;

    private ExportManager $exportManager;

    /** @var list<PhaseInterface> */
    private array $phases = [];

    public function __construct(string $basePath)
    {
        $this->config = new Config($basePath);
        $this->logger = new Logger(
            $basePath . '/logs',
            $this->config->getLogLevel(),
        );
        $this->db = new Database($this->config);
        $this->api = new WhmcsApi($this->config, $this->logger);
        $this->state = new StateManager($basePath . '/state');
        $this->encryptor = new Encryptor($this->config->getLaravelAppKey());
        $this->validator = new Validator($this->db, $this->logger);
        $this->exportManager = new ExportManager($basePath);

        $this->registerPhases();
    }

    /**
     * Allow overriding config values (e.g., --dry-run from CLI).
     */
    public function getConfig(): Config
    {
        return $this->config;
    }

    /**
     * Run all phases or a specific phase by number.
     */
    public function run(?int $phaseNumber = null): void
    {
        $modeLabel = '';

        if ($this->config->isDryRun()) {
            $modeLabel = ' [DRY RUN]';
        } elseif ($this->config->isExportOnly()) {
            $modeLabel = ' [EXPORT ONLY]';
        } elseif ($this->config->isFromExport()) {
            $modeLabel = ' [FROM EXPORT]';
        }

        $this->logger->section("WHMCS to EZSCALE Migration{$modeLabel}");
        $this->logger->info('Started at ' . date('Y-m-d H:i:s'));
        $this->logger->info('Batch size: ' . $this->config->getBatchSize());

        $phasesToRun = $this->phases;

        if ($phaseNumber !== null) {
            $phasesToRun = array_filter(
                $this->phases,
                fn (PhaseInterface $phase): bool => $phase->getPhaseNumber() === $phaseNumber,
            );

            if (empty($phasesToRun)) {
                throw new RuntimeException("Phase {$phaseNumber} is not registered.");
            }
        }

        foreach ($phasesToRun as $phase) {
            $name = $phase->getName();
            $number = $phase->getPhaseNumber();

            // In export-only mode, skip the "already complete" check
            if (! $this->config->isExportOnly() && $phase->shouldSkip()) {
                $this->logger->info("Phase {$number} ({$name}): already complete, skipping.");

                continue;
            }

            $this->logger->info("Phase {$number} ({$name}): validating...");

            if (! $phase->validate()) {
                $this->logger->error("Phase {$number} ({$name}): validation failed. Stopping.");

                return;
            }

            $this->logger->info("Phase {$number} ({$name}): starting...");

            try {
                $phase->run();
            } catch (Throwable $e) {
                $this->logger->error("Phase {$number} ({$name}): FAILED — {$e->getMessage()}", [
                    'exception' => $e::class,
                    'file' => $e->getFile(),
                    'line' => $e->getLine(),
                ]);
                $this->state->save();

                throw $e;
            }
        }

        // Close all export file handles
        $this->exportManager->closeAllHandles();

        if ($this->config->isExportOnly()) {
            $this->logger->section('Export Complete');
            $this->showExportSummary();
        } else {
            $this->logger->section('Migration Complete');
            $this->showStatus();
        }
    }

    /**
     * Print the current progress table for all phases.
     */
    public function showStatus(): void
    {
        $this->logger->info('');
        $this->logger->info(str_pad('Phase', 8) . str_pad('Name', 40) . str_pad('Status', 14) . str_pad('Count', 10) . str_pad('Errors', 10));
        $this->logger->info(str_repeat('-', 82));

        foreach ($this->phases as $phase) {
            $number = $phase->getPhaseNumber();
            $name = $phase->getName();
            $progress = $this->state->getProgress($phase->getPhaseKey());

            $this->logger->info(
                str_pad((string) $number, 8)
                . str_pad($name, 40)
                . str_pad($progress['status'], 14)
                . str_pad((string) $progress['count'], 10)
                . str_pad((string) $progress['errors'], 10),
            );
        }

        $this->logger->info('');

        // Show mapping counts
        $entityTypes = ['clients', 'products', 'services', 'subscriptions', 'invoices', 'transactions', 'orders', 'promotions'];

        foreach ($entityTypes as $entity) {
            $mappingCount = $this->state->getMappingCount($entity);

            if ($mappingCount > 0) {
                $this->logger->info("  {$entity} mappings: {$mappingCount}");
            }
        }
    }

    /**
     * Print a summary of exported JSONL files.
     */
    public function showExportSummary(): void
    {
        $summary = $this->exportManager->getSummary();

        if (empty($summary)) {
            $this->logger->info('No export files found.');

            return;
        }

        $this->logger->info('');
        $this->logger->info(str_pad('Entity', 25) . str_pad('Records', 12) . str_pad('Size', 15) . 'File');
        $this->logger->info(str_repeat('-', 72));

        $totalRecords = 0;

        foreach ($summary as $entity => $info) {
            $totalRecords += $info['records'];
            $this->logger->info(
                str_pad($entity, 25)
                . str_pad(number_format($info['records']), 12)
                . str_pad($info['size'], 15)
                . $info['file'],
            );
        }

        $this->logger->info(str_repeat('-', 72));
        $this->logger->info(str_pad('Total', 25) . number_format($totalRecords));
        $this->logger->info('');
        $this->logger->info("Export directory: {$this->exportManager->getExportDir()}");
        $this->logger->info('To import from these files, run: php migrate.php --from-export');
    }

    /**
     * Reset all migration state (mappings and progress).
     */
    public function reset(): void
    {
        $this->logger->warning('Resetting all migration state...');
        $this->state->reset();
        $this->logger->info('Migration state has been reset.');
    }

    /**
     * Reset all exported JSONL files.
     */
    public function resetExports(): void
    {
        $this->logger->warning('Resetting all export files...');
        $this->exportManager->resetAll();
        $this->logger->info('Export files have been deleted.');
    }

    /**
     * Run pre-flight validation only (no data changes).
     */
    public function validateOnly(): void
    {
        $this->logger->section('Validation Only');

        $ok = $this->validator->validateAll($this->api);

        if ($ok) {
            $this->logger->info('All validations passed. Ready to migrate.');
        } else {
            $this->logger->error('Validation failed. Fix the issues above before running the migration.');
        }
    }

    /**
     * Register all migration phases in execution order.
     */
    private function registerPhases(): void
    {
        $deps = [
            $this->db,
            $this->api,
            $this->state,
            $this->logger,
            $this->config,
            $this->encryptor,
            $this->exportManager,
        ];

        $this->phases = [
            new Phase1Clients(...$deps),
            new Phase2Products(...$deps),
            new Phase3Services(...$deps),
            new Phase4Invoices(...$deps),
            new Phase5Transactions(...$deps),
            new Phase6Coupons(...$deps),
            new Phase7Orders(...$deps),
        ];
    }
}
