<?php

declare(strict_types=1);

namespace WhmcsMigrate\Phases;

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

final class Phase5Transactions extends AbstractPhase
{
    public function getPhaseNumber(): int
    {
        return 5;
    }

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

    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 5 already complete, skipping');

            return;
        }

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

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

            return;
        }

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

            return;
        }

        $this->runFromApi();
    }

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

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

        $this->logger->info("Found {$totalTransactions} transactions in WHMCS");

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

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

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

            $transactions = $response['transactions']['transaction'] ?? [];

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

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

            $tracker->setCurrent($offset);
        }

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

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

            return;
        }

        $this->markStarted();

        $totalTransactions = $this->exportManager->countRecords('transactions');
        $this->logger->info("Found {$totalTransactions} transaction records in export");

        if ($totalTransactions === 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 txns', $totalTransactions);
        $tracker->setCurrent($processedCount);

        $currentIndex = 0;

        foreach ($this->exportManager->readRecords('transactions') as $transaction) {
            $currentIndex++;

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

            $whmcsTxId = (int) ($transaction['id'] ?? 0);

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

                continue;
            }

            try {
                $result = $this->migrateTransaction($whmcsTxId, $transaction);

                if ($result) {
                    $count++;
                }
            } catch (Throwable $e) {
                $errors++;
                $this->logger->error("Failed to migrate transaction {$whmcsTxId}", [
                    '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 5 complete: {$count} transactions migrated, {$errors} errors (elapsed: {$tracker->getElapsed()})");
    }

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

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

        $this->logger->info("Found {$totalTransactions} transactions in WHMCS");

        if ($totalTransactions === 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 txns', $totalTransactions);
        $tracker->setCurrent($offset);

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

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

            $transactions = $response['transactions']['transaction'] ?? [];

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

                break;
            }

            foreach ($transactions as $transaction) {
                $whmcsTxId = (int) ($transaction['id'] ?? 0);

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

                    continue;
                }

                try {
                    $result = $this->migrateTransaction($whmcsTxId, $transaction);

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

                $tracker->advance();
            }

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

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

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

    /**
     * Migrate a single WHMCS transaction to EZSCALE.
     */
    private function migrateTransaction(int $whmcsTxId, array $txData): bool
    {
        if ($this->state->getMapping('transactions', $whmcsTxId) !== null) {
            $this->logger->debug("Transaction {$whmcsTxId} already mapped, skipping");

            return false;
        }

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

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

            return false;
        }

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

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

            return false;
        }

        $amountIn = (float) ($txData['amountin'] ?? '0.00');
        $amountOut = (float) ($txData['amountout'] ?? '0.00');

        if ($amountIn == 0.0 && $amountOut == 0.0) {
            $this->logger->debug("Transaction {$whmcsTxId} has zero amount, skipping");
            $this->logSkipped('transaction', "Zero amount for transaction {$whmcsTxId}");

            return false;
        }

        $transId = trim((string) ($txData['transid'] ?? ''));
        $gatewayTransactionId = $transId !== '' ? $transId : "whmcs_tx_{$whmcsTxId}";

        if ($transId !== '' && $this->db->tableHasRow('payment_transactions', ['gateway_transaction_id' => $gatewayTransactionId])) {
            $this->logger->warning("Transaction with gateway_transaction_id '{$gatewayTransactionId}' already exists in DB, skipping");
            $this->logSkipped('transaction', "Duplicate gateway_transaction_id {$gatewayTransactionId}");

            return false;
        }

        $amount = round($amountIn - $amountOut, 2);
        $status = $amount >= 0 ? 'succeeded' : 'refunded';

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

        $gateway = StatusMapper::mapGateway((string) ($txData['gateway'] ?? ''));
        $description = $this->nullIfEmpty((string) ($txData['description'] ?? ''));
        $createdAt = $this->parseDate((string) ($txData['date'] ?? ''));
        $now = date('Y-m-d H:i:s');

        $metadata = json_encode([
            'whmcs_id' => $whmcsTxId,
            'whmcs_invoiceid' => $whmcsInvoiceId,
            'amountin' => (string) ($txData['amountin'] ?? '0.00'),
            'amountout' => (string) ($txData['amountout'] ?? '0.00'),
        ], JSON_THROW_ON_ERROR);

        if ($this->isDryRun()) {
            $this->logger->info("[DRY RUN] Would create transaction for WHMCS tx {$whmcsTxId}: user={$ezscaleUserId}, amount={$amount}, status={$status}, gateway={$gateway}");

            return true;
        }

        $newTxId = $this->db->insert('payment_transactions', [
            'user_id' => $ezscaleUserId,
            'subscription_id' => null,
            'invoice_id' => $ezscaleInvoiceId,
            'gateway' => $gateway,
            'gateway_transaction_id' => $gatewayTransactionId,
            'amount' => (string) $amount,
            'currency' => 'USD',
            'status' => $status,
            'payment_method' => null,
            'description' => $description,
            'metadata' => $metadata,
            'created_at' => $createdAt ?? $now,
            'updated_at' => $now,
        ]);

        $this->state->setMapping('transactions', $whmcsTxId, $newTxId);

        $this->logger->debug("Migrated transaction {$whmcsTxId} → payment_transaction {$newTxId} (user={$ezscaleUserId}, amount={$amount}, status={$status})");

        return true;
    }

    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' => 'payment_transactions'],
        );

        if ($result === null || (int) $result['cnt'] === 0) {
            $this->logger->error('Validation failed: "payment_transactions" 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('GetTransactions', ['limitstart' => 0, 'limitnum' => 1]);

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

                return false;
            }
        } catch (Throwable $e) {
            $this->logger->error('Validation failed: cannot reach WHMCS 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);
    }
}
