<?php

declare(strict_types=1);

namespace WhmcsMigrate\Phases;

use Throwable;
use WhmcsMigrate\ProgressTracker;

final class Phase6Coupons extends AbstractPhase
{
    public function getPhaseNumber(): int
    {
        return 6;
    }

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

    public function validate(): bool
    {
        // Promotions might not be available in all WHMCS versions,
        // so validation always passes — the run() method handles failures gracefully.
        return true;
    }

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

            return;
        }

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

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

            return;
        }

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

            return;
        }

        $this->runFromApi();
    }

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

        try {
            $response = $this->api->call('GetPromotions');

            if (($response['result'] ?? '') !== 'success') {
                $this->logger->warning('GetPromotions API returned non-success, skipping');

                return;
            }

            $promotions = $response['promotions']['promotion'] ?? [];
        } catch (Throwable $e) {
            $this->logger->warning('GetPromotions API call failed, skipping', [
                'error' => $e->getMessage(),
            ]);

            return;
        }

        $totalPromotions = (int) ($response['totalresults'] ?? count($promotions));
        $this->logger->info("Found {$totalPromotions} promotions in WHMCS");

        if (empty($promotions)) {
            return;
        }

        $this->exportManager->writeBatch('promotions', $promotions);
        $this->logger->info("Exported {$totalPromotions} promotion records to JSONL");
    }

    private function runFromExport(): void
    {
        if (! $this->exportManager->hasExport('promotions')) {
            $this->logger->warning('No promotions export found. Skipping coupon migration.');
            $this->markComplete(0, 0);

            return;
        }

        $this->markStarted();

        $promotions = $this->exportManager->readAll('promotions');
        $totalPromotions = count($promotions);

        $this->logger->info("Found {$totalPromotions} promotions in export");

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

            return;
        }

        $count = 0;
        $errors = 0;
        $tracker = new ProgressTracker('Importing coupons', $totalPromotions);

        foreach ($promotions as $promotion) {
            $whmcsPromoId = (int) ($promotion['id'] ?? 0);

            if ($whmcsPromoId === 0) {
                $this->logger->warning('Skipping promotion with no ID', ['promotion' => $promotion]);
                $errors++;
                $tracker->advance();

                continue;
            }

            try {
                $result = $this->migratePromotion($whmcsPromoId, $promotion);

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

            $tracker->advance();
        }

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

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

        $promotions = [];

        try {
            $response = $this->api->call('GetPromotions');

            if (($response['result'] ?? '') !== 'success') {
                $this->logger->warning('GetPromotions API returned non-success, skipping coupon migration', [
                    'response' => $response,
                ]);
                $this->markComplete(0, 0);

                return;
            }

            $promotions = $response['promotions']['promotion'] ?? [];
        } catch (Throwable $e) {
            $this->logger->warning('GetPromotions API call failed, skipping coupon migration', [
                'error' => $e->getMessage(),
            ]);
            $this->markComplete(0, 0);

            return;
        }

        $totalPromotions = (int) ($response['totalresults'] ?? count($promotions));

        $this->logger->info("Found {$totalPromotions} promotions in WHMCS");

        if (empty($promotions)) {
            $this->markComplete(0, 0);

            return;
        }

        $count = 0;
        $errors = 0;
        $tracker = new ProgressTracker('Migrating coupons', $totalPromotions);

        foreach ($promotions as $promotion) {
            $whmcsPromoId = (int) ($promotion['id'] ?? 0);

            if ($whmcsPromoId === 0) {
                $this->logger->warning('Skipping promotion with no ID', ['promotion' => $promotion]);
                $errors++;
                $tracker->advance();

                continue;
            }

            try {
                $result = $this->migratePromotion($whmcsPromoId, $promotion);

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

            $tracker->advance();
        }

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

    /**
     * Migrate a single WHMCS promotion to an EZSCALE coupon.
     */
    private function migratePromotion(int $whmcsPromoId, array $promoData): bool
    {
        if ($this->state->getMapping('promotions', $whmcsPromoId) !== null) {
            $this->logger->debug("Promotion {$whmcsPromoId} already mapped, skipping");

            return false;
        }

        $code = strtoupper(trim((string) ($promoData['code'] ?? '')));

        if ($code === '') {
            $this->logger->warning("Promotion {$whmcsPromoId} has no code, skipping");
            $this->logSkipped('promotion', "No code for promotion {$whmcsPromoId}");

            return false;
        }

        if ($this->db->tableHasRow('coupons', ['code' => $code])) {
            $this->logger->warning("Coupon with code '{$code}' already exists in DB, skipping");
            $this->logSkipped('promotion', "Duplicate code {$code} for promotion {$whmcsPromoId}");

            return false;
        }

        $whmcsType = trim((string) ($promoData['type'] ?? ''));
        $type = ($whmcsType === 'Percentage') ? 'percentage' : 'fixed';
        $value = (string) ($promoData['value'] ?? '0.00');
        $currency = ($type === 'fixed') ? 'USD' : null;
        $maxUses = (int) ($promoData['maxuses'] ?? 0);
        $maxUsesValue = ($maxUses > 0) ? $maxUses : null;
        $timesUsed = (int) ($promoData['uses'] ?? 0);
        $expirationDate = $this->parseExpirationDate((string) ($promoData['expirationdate'] ?? ''));
        $active = $this->isPromotionActive($expirationDate);
        $now = date('Y-m-d H:i:s');

        if ($this->isDryRun()) {
            $this->logger->info("[DRY RUN] Would create coupon for WHMCS promotion {$whmcsPromoId}: code={$code}, type={$type}, value={$value}, active=" . ($active ? 'yes' : 'no'));

            return true;
        }

        $newCouponId = $this->db->insert('coupons', [
            'code' => $code,
            'type' => $type,
            'value' => $value,
            'currency' => $currency,
            'applies_to' => null,
            'max_uses' => $maxUsesValue,
            'times_used' => $timesUsed,
            'active' => $active ? 1 : 0,
            'expires_at' => $expirationDate,
            'created_at' => $now,
            'updated_at' => $now,
        ]);

        $this->state->setMapping('promotions', $whmcsPromoId, $newCouponId);

        $this->logger->debug("Migrated promotion {$whmcsPromoId} → coupon {$newCouponId} (code={$code}, type={$type}, value={$value})");

        return true;
    }

    private function parseExpirationDate(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}$/', $date)) {
            return $date . ' 23:59:59';
        }

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

        $timestamp = strtotime($date);

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

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

    private function isPromotionActive(?string $expirationDate): bool
    {
        if ($expirationDate === null) {
            return true;
        }

        $expirationTimestamp = strtotime($expirationDate);

        if ($expirationTimestamp === false) {
            return true;
        }

        return $expirationTimestamp > time();
    }
}
