<?php

declare(strict_types=1);

namespace WhmcsMigrate\Phases;

use Throwable;
use WhmcsMigrate\ProgressTracker;
use WhmcsMigrate\StatusMapper;

final class Phase7Orders extends AbstractPhase
{
    /**
     * WHMCS order status -> EZSCALE order status mapping.
     */
    private const array STATUS_MAP = [
        'Active' => 'completed',
        'Pending' => 'pending',
        'Fraud' => 'cancelled',
        'Cancelled' => 'cancelled',
        'Suspended' => 'cancelled',
    ];

    public function getPhaseNumber(): int
    {
        return 7;
    }

    public function getName(): string
    {
        return 'Orders → Orders';
    }

    public function validate(): bool
    {
        if ($this->isExportOnly()) {
            return $this->validateApi();
        }

        if ($this->isFromExport()) {
            return $this->validateDb();
        }

        return $this->validateDb() && $this->validateApi();
    }

    public function run(): void
    {
        if (! $this->isExportOnly() && $this->shouldSkip()) {
            $this->logger->info('Phase 7 already complete, skipping');

            return;
        }

        $this->logger->section('Phase 7: ' . $this->getName());

        if ($this->isExportOnly()) {
            $this->runExport();

            return;
        }

        if ($this->isFromExport()) {
            $this->runFromExport();

            return;
        }

        $this->runFromApi();
    }

    private function runExport(): void
    {
        $this->logger->info('Exporting orders from WHMCS API...');
        $this->exportManager->resetExport('orders');

        $probeResponse = $this->api->call('GetOrders', ['limitstart' => 0, 'limitnum' => 1]);
        $totalOrders = (int) ($probeResponse['totalresults'] ?? 0);

        $this->logger->info("Found {$totalOrders} orders in WHMCS");

        if ($totalOrders === 0) {
            return;
        }

        $batchSize = $this->getBatchSize();
        $offset = 0;
        $exported = 0;
        $tracker = new ProgressTracker('Exporting orders', $totalOrders);

        while ($offset < $totalOrders) {
            $response = $this->api->call('GetOrders', [
                'limitstart' => $offset,
                'limitnum' => $batchSize,
            ]);

            $orders = $response['orders']['order'] ?? [];

            if (empty($orders)) {
                break;
            }

            $this->exportManager->writeBatch('orders', $orders);
            $exported += count($orders);
            $offset += count($orders);

            $tracker->setCurrent($offset);
        }

        $tracker->finish();
        $this->logger->info("Exported {$exported} order records to JSONL");
    }

    private function runFromExport(): void
    {
        if (! $this->exportManager->hasExport('orders')) {
            $this->logger->error('No orders export found. Run --export-only first.');

            return;
        }

        $this->markStarted();

        $totalOrders = $this->exportManager->countRecords('orders');
        $this->logger->info("Found {$totalOrders} order records in export");

        if ($totalOrders === 0) {
            $this->markComplete(0, 0);

            return;
        }

        $progress = $this->state->getProgress($this->getPhaseKey());
        $processedCount = (int) ($progress['offset'] ?? 0);
        $count = (int) ($progress['count'] ?? 0);
        $errors = (int) ($progress['errors'] ?? 0);

        if ($processedCount > 0) {
            $this->logger->info("Resuming from record {$processedCount} ({$count} migrated, {$errors} errors so far)");
        }

        $tracker = new ProgressTracker('Importing orders', $totalOrders);
        $tracker->setCurrent($processedCount);

        $currentIndex = 0;

        foreach ($this->exportManager->readRecords('orders') as $order) {
            $currentIndex++;

            if ($currentIndex <= $processedCount) {
                continue;
            }

            $whmcsOrderId = (int) ($order['id'] ?? 0);

            if ($whmcsOrderId === 0) {
                $errors++;
                $processedCount++;
                $tracker->advance();

                continue;
            }

            try {
                $result = $this->migrateOrder($whmcsOrderId, $order);

                if ($result) {
                    $count++;
                }
            } catch (Throwable $e) {
                $errors++;
                $this->logger->error("Failed to migrate order {$whmcsOrderId}", [
                    'error' => $e->getMessage(),
                ]);
            }

            $processedCount++;
            $tracker->advance();

            if ($processedCount % 100 === 0) {
                $this->state->setProgress($this->getPhaseKey(), 'running', $processedCount, $count, $errors);
                $this->state->save();
            }
        }

        $tracker->finish();
        $this->markComplete($count, $errors);
        $this->logger->info("Phase 7 complete: {$count} orders migrated, {$errors} errors (elapsed: {$tracker->getElapsed()})");
    }

    private function runFromApi(): void
    {
        $this->markStarted();

        $probeResponse = $this->api->call('GetOrders', ['limitstart' => 0, 'limitnum' => 1]);
        $totalOrders = (int) ($probeResponse['totalresults'] ?? 0);

        $this->logger->info("Found {$totalOrders} orders in WHMCS");

        if ($totalOrders === 0) {
            $this->markComplete(0, 0);

            return;
        }

        $progress = $this->state->getProgress($this->getPhaseKey());
        $offset = (int) ($progress['offset'] ?? 0);
        $count = (int) ($progress['count'] ?? 0);
        $errors = (int) ($progress['errors'] ?? 0);
        $batchSize = $this->getBatchSize();

        if ($offset > 0) {
            $this->logger->info("Resuming from offset {$offset} ({$count} migrated, {$errors} errors so far)");
        }

        $tracker = new ProgressTracker('Migrating orders', $totalOrders);
        $tracker->setCurrent($offset);

        while ($offset < $totalOrders) {
            $this->logger->debug("Fetching orders batch: offset={$offset}, limit={$batchSize}");

            $response = $this->api->call('GetOrders', [
                'limitstart' => $offset,
                'limitnum' => $batchSize,
            ]);

            $orders = $response['orders']['order'] ?? [];

            if (empty($orders)) {
                $this->logger->debug('No more orders returned, ending pagination');

                break;
            }

            foreach ($orders as $order) {
                $whmcsOrderId = (int) ($order['id'] ?? 0);

                if ($whmcsOrderId === 0) {
                    $this->logger->warning('Skipping order with no ID', ['order' => $order]);
                    $errors++;

                    continue;
                }

                try {
                    $result = $this->migrateOrder($whmcsOrderId, $order);

                    if ($result) {
                        $count++;
                    }
                } catch (Throwable $e) {
                    $errors++;
                    $this->logger->error("Failed to migrate order {$whmcsOrderId}", [
                        'error' => $e->getMessage(),
                    ]);
                }

                $tracker->advance();
            }

            $offset += count($orders);
            $this->state->setProgress($this->getPhaseKey(), 'running', $offset, $count, $errors);
            $this->state->save();

            $this->logger->info("Batch complete: {$count} migrated, {$errors} errors, offset now {$offset}/{$totalOrders}");
        }

        $tracker->finish();
        $this->markComplete($count, $errors);
        $this->logger->info("Phase 7 complete: {$count} orders migrated, {$errors} errors (elapsed: {$tracker->getElapsed()})");
    }

    /**
     * Migrate a single WHMCS order.
     */
    private function migrateOrder(int $whmcsOrderId, array $orderData): bool
    {
        if ($this->state->getMapping('orders', $whmcsOrderId) !== null) {
            $this->logger->debug("Order {$whmcsOrderId} already mapped, skipping");

            return false;
        }

        $whmcsUserId = (int) ($orderData['userid'] ?? 0);

        if ($whmcsUserId === 0) {
            $this->logger->warning("Order {$whmcsOrderId} has no userid, skipping");
            $this->logSkipped('order', "No userid for order {$whmcsOrderId}");

            return false;
        }

        $ezscaleUserId = $this->state->getMapping('clients', $whmcsUserId);

        if ($ezscaleUserId === null) {
            $this->logger->warning("Order {$whmcsOrderId} belongs to unmapped client {$whmcsUserId}, skipping");
            $this->logSkipped('order', "Unmapped client {$whmcsUserId} for order {$whmcsOrderId}");

            return false;
        }

        $orderNum = trim((string) ($orderData['ordernum'] ?? ''));
        $orderNumber = $orderNum !== '' ? "WHMCS-{$orderNum}" : "WHMCS-ORD-{$whmcsOrderId}";

        if ($this->db->tableHasRow('orders', ['order_number' => $orderNumber])) {
            $this->logger->warning("Order with order_number '{$orderNumber}' already exists in DB, skipping");
            $this->logSkipped('order', "Duplicate order_number {$orderNumber}");

            return false;
        }

        $planId = $this->resolvePlanId($orderData);

        $whmcsInvoiceId = (int) ($orderData['invoiceid'] ?? 0);
        $ezscaleInvoiceId = $whmcsInvoiceId > 0
            ? $this->state->getMapping('invoices', $whmcsInvoiceId)
            : null;

        $whmcsStatus = (string) ($orderData['status'] ?? 'Pending');
        $status = self::STATUS_MAP[$whmcsStatus] ?? 'pending';

        $total = (string) ($orderData['amount'] ?? '0.00');
        $gateway = StatusMapper::mapGateway((string) ($orderData['paymentmethod'] ?? ''));
        $notes = $this->nullIfEmpty((string) ($orderData['notes'] ?? ''));
        $adminNotes = "Imported from WHMCS. Order ID: {$whmcsOrderId}" . ($notes !== null ? ". Notes: {$notes}" : '');
        $createdAt = $this->parseDate((string) ($orderData['date'] ?? ''));
        $now = date('Y-m-d H:i:s');

        $isCompleted = $status === 'completed';
        $isCancelled = $status === 'cancelled';

        if ($this->isDryRun()) {
            $this->logger->info("[DRY RUN] Would create order for WHMCS order {$whmcsOrderId}: user={$ezscaleUserId}, total={$total}, status={$status}");

            return true;
        }

        $newOrderId = $this->db->insert('orders', [
            'user_id' => $ezscaleUserId,
            'plan_id' => $planId,
            'invoice_id' => $ezscaleInvoiceId,
            'service_id' => null,
            'order_number' => $orderNumber,
            'status' => $status,
            'total' => $total,
            'currency' => 'USD',
            'payment_gateway' => $gateway,
            'configuration' => null,
            'admin_notes' => mb_strlen($adminNotes) > 65535 ? mb_substr($adminNotes, 0, 65532) . '...' : $adminNotes,
            'completed_at' => $isCompleted ? ($createdAt ?? $now) : null,
            'cancelled_at' => $isCancelled ? $now : null,
            'created_at' => $createdAt ?? $now,
            'updated_at' => $now,
        ]);

        $this->state->setMapping('orders', $whmcsOrderId, $newOrderId);

        $this->logger->debug("Migrated order {$whmcsOrderId} → order {$newOrderId} (user={$ezscaleUserId}, total={$total}, status={$status})");

        return true;
    }

    private function resolvePlanId(array $orderData): int
    {
        $lineItems = $orderData['lineitems']['lineitem'] ?? [];

        if (is_array($lineItems)) {
            foreach ($lineItems as $item) {
                $pid = (int) ($item['productid'] ?? $item['pid'] ?? 0);

                if ($pid > 0) {
                    $planId = $this->state->getMapping('products', $pid);

                    if ($planId !== null) {
                        return $planId;
                    }
                }
            }
        }

        $fallback = $this->db->queryOne("SELECT `id` FROM `plans` LIMIT 1");

        return $fallback !== null ? (int) $fallback['id'] : 1;
    }

    private function validateDb(): bool
    {
        $result = $this->db->queryOne(
            "SELECT COUNT(*) AS cnt FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = :table",
            ['table' => 'orders'],
        );

        if ($result === null || (int) $result['cnt'] === 0) {
            $this->logger->error('Validation failed: "orders" table does not exist');

            return false;
        }

        $clientCount = $this->state->getMappingCount('clients');

        if ($clientCount === 0) {
            $this->logger->error('Validation failed: no client mappings found — Phase 1 must run first');

            return false;
        }

        return true;
    }

    private function validateApi(): bool
    {
        try {
            $response = $this->api->call('GetOrders', ['limitstart' => 0, 'limitnum' => 1]);

            if (($response['result'] ?? '') !== 'success') {
                $this->logger->error('Validation failed: WHMCS GetOrders API returned non-success', [
                    'response' => $response,
                ]);

                return false;
            }
        } catch (Throwable $e) {
            $this->logger->error('Validation failed: cannot reach WHMCS GetOrders API', [
                'error' => $e->getMessage(),
            ]);

            return false;
        }

        return true;
    }

    private function nullIfEmpty(string $value): ?string
    {
        $trimmed = trim($value);

        return $trimmed === '' ? null : $trimmed;
    }

    private function parseDate(string $date): ?string
    {
        $date = trim($date);

        if ($date === '' || str_starts_with($date, '0000-00-00')) {
            return null;
        }

        if (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/', $date)) {
            return $date;
        }

        if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $date)) {
            return $date . ' 00:00:00';
        }

        $timestamp = strtotime($date);

        if ($timestamp === false) {
            return null;
        }

        return date('Y-m-d H:i:s', $timestamp);
    }
}
