<?php
declare(strict_types=1);

namespace App\Modules\PlayerAuth\Services;

use App\Core\App;
use App\Core\Database;

final class PlayerAuthService
{
    private Database $db;

    public function __construct()
    {
        $this->db = App::getInstance()->db();
    }

    public function findPlayerByPhone(string $phone): ?array
    {
        return $this->db->selectOne(
            "SELECT id, name_ar, phone, password_hash, is_archived FROM players WHERE phone = ? AND is_archived = 0",
            [$phone]
        );
    }

    public function verifyPassword(array $player, string $password): bool
    {
        if (empty($player['password_hash'])) {
            return false;
        }
        return password_verify($password, $player['password_hash']);
    }

    public function generateOtp(int $playerId): string
    {
        $otp = str_pad((string) random_int(0, 999999), 6, '0', STR_PAD_LEFT);
        $expiresAt = date('Y-m-d H:i:s', time() + 300); // 5 minutes

        $this->db->update('players', [
            'otp_code' => $otp,
            'otp_expires_at' => $expiresAt,
        ], 'id = ?', [$playerId]);

        return $otp;
    }

    public function verifyOtp(array $player, string $otp): bool
    {
        if (empty($player['otp_code']) || $player['otp_code'] !== $otp) {
            return false;
        }

        $row = $this->db->selectOne(
            "SELECT otp_expires_at FROM players WHERE id = ?",
            [$player['id']]
        );

        if (!$row || !$row['otp_expires_at'] || $row['otp_expires_at'] < date('Y-m-d H:i:s')) {
            return false;
        }

        $this->db->update('players', [
            'otp_code' => null,
            'otp_expires_at' => null,
        ], 'id = ?', [$player['id']]);

        return true;
    }

    public function createToken(int $playerId, string $name = 'mobile', ?string $deviceInfo = null): array
    {
        $config = App::getInstance()->config('api.token', []);
        $maxTokens = $config['max_per_player'] ?? 5;
        $ttlHours = $config['player_ttl_hours'] ?? 2160; // 90 days

        $activeCount = $this->db->selectOne(
            "SELECT COUNT(*) as cnt FROM player_tokens WHERE player_id = ? AND is_revoked = 0 AND (expires_at IS NULL OR expires_at > NOW())",
            [$playerId]
        );

        if (($activeCount['cnt'] ?? 0) >= $maxTokens) {
            $this->db->query(
                "UPDATE player_tokens SET is_revoked = 1 WHERE player_id = ? AND is_revoked = 0 ORDER BY last_used_at ASC LIMIT 1",
                [$playerId]
            );
        }

        $token = bin2hex(random_bytes(32));
        $expiresAt = date('Y-m-d H:i:s', time() + ($ttlHours * 3600));

        $this->db->insert('player_tokens', [
            'player_id' => $playerId,
            'token' => $token,
            'name' => $name,
            'device_info' => $deviceInfo,
            'expires_at' => $expiresAt,
        ]);

        $this->db->update('players', [
            'last_login_at' => date('Y-m-d H:i:s'),
        ], 'id = ?', [$playerId]);

        return [
            'token' => $token,
            'expires_at' => $expiresAt,
        ];
    }

    public function revokeToken(string $token): void
    {
        $this->db->update('player_tokens', ['is_revoked' => 1], '`token` = ?', [$token]);
    }

    public function refreshToken(string $currentToken, int $playerId): array
    {
        $this->revokeToken($currentToken);
        return $this->createToken($playerId);
    }
}
