<?php

namespace App\Console\Commands;

use App\Services\Api\API;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;

class SyncFactionRanking extends Command
{
    protected $signature = 'factions:sync
                            {--limit=0 : Maximum number of factions to sync (0 = no limit)}
                            {--batch=50 : Process batch size}
                            {--delay=50 : Delay in milliseconds between API calls}
                            {--start-id=1 : Starting faction ID}';

    protected $description = 'Sync faction rankings from game API to database.';

    protected API $api;

    protected int $maxNotFound = 100;

    public function __construct(API $api)
    {
        parent::__construct();
        $this->api = $api;
    }

    public function handle(): int
    {
        $limit = (int) $this->option('limit');
        $batchSize = (int) $this->option('batch');
        $delay = (int) $this->option('delay');
        $startId = max((int) $this->option('start-id'), 1);

        $this->info('========================================');
        $this->info('  Faction Ranking Sync');
        $this->info('========================================');
        $this->info("Batch Size: {$batchSize}");
        $this->info("Delay: {$delay}ms");
        $this->info("Start ID: {$startId}");

        $this->info("\nScanning factions from API...");

        $totalSynced = 0;
        $totalErrors = 0;
        $totalSkipped = 0;
        $notFoundCount = 0;
        $batchData = [];

        for ($currentId = $startId; $limit <= 0 || $totalSynced < $limit; $currentId++) {
            if ($notFoundCount >= $this->maxNotFound) {
                $this->info("\nStopped scanning: {$this->maxNotFound} consecutive empty factions found.");
                break;
            }

            try {
                $factionData = $this->fetchFactionData($currentId, $delay);

                if ($factionData) {
                    $notFoundCount = 0;
                    $batchData[] = $factionData;
                    $totalSynced++;

                    if ($totalSynced % 50 === 0) {
                        $this->line("  → Synced {$totalSynced} factions so far...");
                    }
                } else {
                    $notFoundCount++;
                    $totalSkipped++;
                }
            } catch (\Exception $e) {
                $totalErrors++;
                $notFoundCount++;
                Log::error("Failed to sync faction {$currentId}: {$e->getMessage()}");
            }

            if (count($batchData) >= $batchSize) {
                $this->flushBatch($batchData);
                $batchData = [];
                gc_collect_cycles();
            }

            if ($delay > 0) {
                usleep($delay * 1000);
            }
        }

        if (!empty($batchData)) {
            $this->flushBatch($batchData);
        }

        $this->info("\nRecalculating faction total PK counts...");
        $this->recalculateFactionPkCounts();

        $this->displaySummary($totalSynced, $totalErrors, $totalSkipped);

        return Command::SUCCESS;
    }

    protected function fetchFactionData(int $factionId, int $delay): ?array
    {
        $info = $this->api->getFactionInfo($factionId);

        if (!$info || empty($info['fid']) || empty($info['name'])) {
            return null;
        }

        $members = $info['member'] ?? [];
        $memberCount = is_array($members) ? count($members) : 0;

        $masterName = null;
        $masterRoleId = null;
        $master = $info['master'] ?? null;

        if ($master && isset($master['roleid'])) {
            $masterRoleId = $master['roleid'];
            try {
                $masterBase = $this->api->getRoleBase($masterRoleId);
                if ($masterBase) {
                    $masterName = $masterBase['name'] ?? null;
                }
            } catch (\Exception $e) {
            }
        }

        $level = $info['level'] ?? 0;
        if ($level === 2) {
            $level = 3;
        } elseif ($level === 0) {
            $level = 1;
        }

        return [
            'faction_id' => $info['fid'],
            'faction_name' => $info['name'],
            'level' => $level,
            'member_count' => $memberCount,
            'master_name' => $masterName,
            'master_role_id' => $masterRoleId,
            'announce' => $info['announce'] ?? null,
            'last_update' => now(),
            'created_at' => now(),
            'updated_at' => now(),
        ];
    }

    protected function flushBatch(array $data): void
    {
        if (empty($data)) {
            return;
        }

        $this->bulkUpsertFactions($data);
    }

    protected function bulkUpsertFactions(array $data): void
    {
        $columns = ['faction_id', 'faction_name', 'level', 'member_count', 'master_name', 'master_role_id', 'announce', 'last_update', 'created_at', 'updated_at'];
        $updateColumns = ['faction_name', 'level', 'member_count', 'master_name', 'master_role_id', 'announce', 'last_update', 'updated_at'];

        try {
            $values = [];
            $bindings = [];

            foreach ($data as $row) {
                $placeholders = array_fill(0, count($columns), '?');
                $values[] = '('.implode(', ', $placeholders).')';
                foreach ($columns as $col) {
                    $bindings[] = $row[$col];
                }
            }

            $sql = "INSERT INTO faction_rankings (".implode(', ', $columns).') VALUES '
                .implode(', ', $values)
                .' ON DUPLICATE KEY UPDATE '
                .implode(', ', array_map(fn ($col) => "{$col} = VALUES({$col})", $updateColumns));

            DB::statement($sql, $bindings);
            $this->line('  → Upserted '.count($data).' faction records');
        } catch (\Exception $e) {
            $this->error('  → Failed to bulk upsert: '.$e->getMessage());
            Log::error('Faction bulk upsert failed: '.$e->getMessage());
        }
    }

    protected function recalculateFactionPkCounts(): void
    {
        try {
            DB::statement(
                "UPDATE faction_rankings fr SET total_pk_count = (
                    SELECT COALESCE(SUM(pk_count), 0) FROM player_rankings pr WHERE pr.faction_id = fr.faction_id
                )"
            );
            $this->line('  → Recalculated total PK counts for all factions');
        } catch (\Exception $e) {
            $this->error('  → Failed to recalculate faction PK counts: '.$e->getMessage());
            Log::error('Faction PK count recalculation failed: '.$e->getMessage());
        }
    }

    protected function displaySummary(int $synced, int $errors, int $skipped): void
    {
        $this->newLine(2);
        $this->info('========================================');
        $this->info('  Sync Summary');
        $this->info('========================================');
        $this->info("Synced: {$synced}");
        $this->info("Errors: {$errors}");
        $this->info("Skipped: {$skipped}");
        $this->info('========================================');
    }
}
