<?php

namespace App\Console\Commands;

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

class SyncPlayerRanking extends Command
{
    protected $signature = 'rankings:sync';

    protected $description = 'Sync player rankings from game API for registered users.';

    protected API $api;

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

    public function handle(): int
    {
        $this->info('Syncing player rankings...');

        $userIds = DB::table('users')->orderBy('ID')->pluck('ID')->toArray();

        if (empty($userIds)) {
            $this->warn('No registered users found.');

            return Command::SUCCESS;
        }

        $this->info('Found '.count($userIds).' users');

        $bar = $this->output->createProgressBar(count($userIds));
        $bar->start();

        $synced = 0;
        $errors = 0;
        $skipped = 0;
        $batchData = [];

        foreach ($userIds as $userId) {
            try {
                $roles = $this->api->getRoles($userId);
                $roleList = $roles['roles'] ?? [];

                foreach ($roleList as $role) {
                    $roleId = $role['id'] ?? null;
                    if (!$roleId) {
                        continue;
                    }

                    $data = $this->fetchRoleData($roleId, $userId);
                    if ($data) {
                        $batchData[] = $data;
                        $synced++;
                    }
                }

                if (empty($roleList)) {
                    $skipped++;
                }

                if (count($batchData) >= 50) {
                    $this->flushBatch($batchData);
                    $batchData = [];
                }
            } catch (\Exception $e) {
                $errors++;
                Log::error("Failed to sync user {$userId}: {$e->getMessage()}");
            }

            $bar->advance();
        }

        $bar->finish();

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

        $this->newLine();
        $this->info("Synced: {$synced} | Errors: {$errors} | Skipped: {$skipped}");

        return Command::SUCCESS;
    }

    protected function fetchRoleData(int $roleId, int $userId): ?array
    {
        $base = $this->api->getRoleBase($roleId);

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

        if ((int) $base['userid'] !== $userId) {
            return null;
        }

        $status = $this->api->getRoleStatus($roleId);

        $factionId = null;
        $factionName = null;

        try {
            $userFaction = $this->api->getUserFaction($roleId);
            if ($userFaction && ($fid = $userFaction['fid'] ?? 0) > 0) {
                $factionId = $fid;
                $factionInfo = $this->api->getFactionInfo($fid);
                if ($factionInfo) {
                    $factionName = $factionInfo['name'] ?? null;
                }
            }
        } catch (\Exception $e) {
        }

        return [
            'role_id' => $roleId,
            'user_id' => $userId,
            'role_name' => $base['name'],
            'cls' => $base['cls'] ?? 0,
            'level' => $status['level'] ?? 0,
            'reputation' => $status['reputation'] ?? 0,
            'pk_count' => $this->extractPkCount($status),
            'faction_id' => $factionId,
            'faction_name' => $factionName,
            'last_update' => now(),
            'created_at' => now(),
            'updated_at' => now(),
        ];
    }

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

        $columns = ['role_id', 'user_id', 'role_name', 'cls', 'level', 'reputation', 'pk_count', 'faction_id', 'faction_name', 'last_update', 'created_at', 'updated_at'];

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

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

            $updateCols = array_diff($columns, ['role_id', 'created_at']);

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

            DB::statement($sql, $bindings);
        } catch (\Exception $e) {
            Log::error('Bulk upsert failed: '.$e->getMessage());
        }

        $factionIds = array_unique(array_filter(array_column($data, 'faction_id')));

        foreach ($factionIds as $factionId) {
            try {
                DB::statement(
                    "UPDATE faction_rankings SET total_pk_count = (
                        SELECT COALESCE(SUM(pk_count), 0) FROM player_rankings WHERE faction_id = ?
                    ) WHERE faction_id = ?",
                    [$factionId, $factionId]
                );
            } catch (\Exception $e) {
                Log::error("Failed to update faction PK count for faction {$factionId}: {$e->getMessage()}");
            }
        }
    }

    protected function extractPkCount(array $status): int
    {
        if (empty($status['var_data']) || !is_string($status['var_data'])) {
            return 0;
        }

        $hex = $status['var_data'];
        if (strlen($hex) < 16) {
            return 0;
        }

        return (int) hexdec(implode('', array_reverse(str_split(substr($hex, 8, 8), 2))));
    }
}
