<?php

declare(strict_types=1);

require_once dirname(__DIR__, 2) . '/core/auth.php';
require_once dirname(__DIR__, 2) . '/core/csrf.php';
require_once dirname(__DIR__, 2) . '/core/validation.php';
require_once dirname(__DIR__, 2) . '/core/audit.php';
require_once __DIR__ . '/../players/helpers.php';
require_once __DIR__ . '/../fixtures/helpers.php';

function license_module_boot(): void
{
    require_login();
    require_permission('manage_player_licenses');
}

function license_verification_module_boot(): void
{
    require_login();

    if (!can_manage_player_licenses() && !can_verify_player_licenses()) {
        abort_request(403, 'Access denied', 'Your account does not have permission to verify player licences.');
    }
}

function can_manage_player_licenses(): bool
{
    return can('manage_player_licenses');
}

function can_verify_player_licenses(): bool
{
    return can('verify_player_licenses');
}

function player_license_tables_ready(): bool
{
    static $ready = null;

    if ($ready !== null) {
        return $ready;
    }

    try {
        $ready = (int) db_fetch_value(
            'SELECT COUNT(*)
             FROM information_schema.tables
             WHERE table_schema = DATABASE()
               AND table_name IN ("player_licenses", "player_license_checks")',
            [],
            0
        ) === 2;
    } catch (\Throwable) {
        $ready = false;
    }

    return $ready;
}

function player_license_status_label(string $status): string
{
    return match ($status) {
        'active' => 'Active',
        'suspended' => 'Suspended',
        'revoked' => 'Revoked',
        'expired' => 'Expired',
        default => 'Not issued',
    };
}

function player_license_badge_class(string $status): string
{
    return match ($status) {
        'active' => 'bg-success-subtle text-success-emphasis',
        'suspended' => 'bg-warning-subtle text-warning-emphasis',
        'revoked' => 'bg-danger-subtle text-danger-emphasis',
        'expired' => 'bg-secondary-subtle text-secondary-emphasis',
        default => 'bg-light text-dark',
    };
}

function player_license_effective_status(?array $license): string
{
    if ($license === null || !isset($license['id'])) {
        return 'not_issued';
    }

    $status = (string) ($license['status'] ?? 'active');
    $expiresAt = trim((string) ($license['expires_at'] ?? ''));

    if ($status === 'active' && $expiresAt !== '') {
        $expiresTimestamp = strtotime($expiresAt . ' 23:59:59');
        if ($expiresTimestamp !== false && $expiresTimestamp < time()) {
            return 'expired';
        }
    }

    return $status;
}

function player_license_supports_image_document(?string $path): bool
{
    if (!is_string($path) || trim($path) === '') {
        return false;
    }

    $extension = strtolower((string) pathinfo($path, PATHINFO_EXTENSION));

    return in_array($extension, ['jpg', 'jpeg', 'png', 'webp', 'gif'], true);
}

function player_license_passport_image_path(array $playerRecord): ?string
{
    $playerPhoto = trim((string) ($playerRecord['photo'] ?? ''));
    if ($playerPhoto !== '' && player_license_supports_image_document($playerPhoto)) {
        return $playerPhoto;
    }

    $passportDocument = db_fetch_one(
        'SELECT file_path
         FROM player_documents
         WHERE player_id = :player_id
           AND document_type IN ("passport", "id_card")
         ORDER BY uploaded_at DESC, id DESC
         LIMIT 1',
        ['player_id' => (int) $playerRecord['id']]
    );

    $passportPath = trim((string) ($passportDocument['file_path'] ?? ''));

    return player_license_supports_image_document($passportPath) ? $passportPath : null;
}

function player_license_is_verifiable(?array $license): bool
{
    return player_license_effective_status($license) === 'active';
}

function player_license_default_expiry_date(): string
{
    $seasonEnd = db_fetch_value(
        'SELECT end_date
         FROM seasons
         WHERE status IN ("active", "open")
           AND end_date IS NOT NULL
         ORDER BY end_date ASC
         LIMIT 1',
        [],
        null
    );

    if (is_string($seasonEnd) && trim($seasonEnd) !== '') {
        return $seasonEnd;
    }

    return date('Y-m-d', strtotime('+1 year'));
}

function player_license_generate_number(array $playerRecord): string
{
    $registrationNumber = strtoupper(preg_replace('/[^A-Z0-9]+/', '', (string) ($playerRecord['registration_number'] ?? 'PLAYER')) ?: 'PLAYER');

    return 'JLFC-LIC-' . $registrationNumber;
}

function player_license_generate_verification_code(): string
{
    for ($attempt = 0; $attempt < 5; $attempt++) {
        $code = strtoupper(bin2hex(random_bytes(4)));
        $exists = (int) db_fetch_value(
            'SELECT COUNT(*) FROM player_licenses WHERE verification_code = :verification_code',
            ['verification_code' => $code],
            0
        );

        if ($exists === 0) {
            return $code;
        }
    }

    return strtoupper(substr(sha1(uniqid((string) mt_rand(), true)), 0, 8));
}

function get_player_license_by_player_id(int $playerId): ?array
{
    if (!player_license_tables_ready()) {
        return null;
    }

    return db_fetch_one(
        'SELECT pl.*, issuer.full_name AS issued_by_name
         FROM player_licenses pl
         LEFT JOIN users issuer ON issuer.id = pl.issued_by_user_id
         WHERE pl.player_id = :player_id
         LIMIT 1',
        ['player_id' => $playerId]
    ) ?: null;
}

function player_license_list(): array
{
    if (!player_license_tables_ready()) {
        return [];
    }

    return db_fetch_all(
        'SELECT p.id AS player_id,
                p.first_name,
                p.last_name,
                p.registration_number,
                p.eligibility_status,
                p.status AS player_status,
                c.name AS club_name,
                pl.id AS license_id,
                pl.license_number,
                pl.verification_code,
                pl.status AS license_status,
                pl.issued_at,
                pl.expires_at
         FROM players p
         INNER JOIN clubs c ON c.id = p.club_id
         LEFT JOIN player_licenses pl ON pl.player_id = p.id
         ORDER BY c.name ASC, p.last_name ASC, p.first_name ASC'
    );
}

function issue_player_license(int $playerId, int $issuedByUserId): int
{
    $playerRecord = find_player_or_abort($playerId);
    $existingLicense = get_player_license_by_player_id($playerId);
    $licenseNumber = $existingLicense['license_number'] ?? player_license_generate_number($playerRecord);
    $verificationCode = player_license_generate_verification_code();
    $expiresAt = player_license_default_expiry_date();

    if ($existingLicense === null) {
        db_query(
            'INSERT INTO player_licenses (player_id, license_number, verification_code, status, issued_by_user_id, issued_at, expires_at, notes, created_at, updated_at)
             VALUES (:player_id, :license_number, :verification_code, :status, :issued_by_user_id, NOW(), :expires_at, NULL, NOW(), NOW())',
            [
                'player_id' => $playerId,
                'license_number' => $licenseNumber,
                'verification_code' => $verificationCode,
                'status' => 'active',
                'issued_by_user_id' => $issuedByUserId,
                'expires_at' => $expiresAt,
            ]
        );
    } else {
        db_query(
            'UPDATE player_licenses
             SET license_number = :license_number,
                 verification_code = :verification_code,
                 status = :status,
                 issued_by_user_id = :issued_by_user_id,
                 issued_at = NOW(),
                 expires_at = :expires_at,
                 updated_at = NOW()
             WHERE player_id = :player_id',
            [
                'license_number' => $licenseNumber,
                'verification_code' => $verificationCode,
                'status' => 'active',
                'issued_by_user_id' => $issuedByUserId,
                'expires_at' => $expiresAt,
                'player_id' => $playerId,
            ]
        );
    }

    $licenseId = (int) db_fetch_value(
        'SELECT id FROM player_licenses WHERE player_id = :player_id LIMIT 1',
        ['player_id' => $playerId],
        0
    );

    db_query(
        'DELETE FROM player_license_checks
         WHERE player_id = :player_id',
        ['player_id' => $playerId]
    );

    return $licenseId;
}

function set_player_license_status(int $playerId, string $status, int $updatedByUserId): void
{
    $license = get_player_license_by_player_id($playerId);

    if ($license === null) {
        throw new RuntimeException('No player licence exists for this player.');
    }

    db_query(
        'UPDATE player_licenses
         SET status = :status,
             issued_by_user_id = :issued_by_user_id,
             updated_at = NOW()
         WHERE player_id = :player_id',
        [
            'status' => $status,
            'issued_by_user_id' => $updatedByUserId,
            'player_id' => $playerId,
        ]
    );

    if ($status !== 'active') {
        db_query(
            'DELETE FROM player_license_checks
             WHERE player_id = :player_id',
            ['player_id' => $playerId]
        );
    }
}

function player_license_recent_checks(int $playerId): array
{
    if (!player_license_tables_ready()) {
        return [];
    }

    return db_fetch_all(
        'SELECT plc.verified_at,
                verifier.full_name AS verified_by_name,
                f.id AS fixture_id,
                f.match_date,
                f.match_time,
                home.name AS home_club_name,
                away.name AS away_club_name
         FROM player_license_checks plc
         INNER JOIN fixtures f ON f.id = plc.fixture_id
         INNER JOIN clubs home ON home.id = f.home_club_id
         INNER JOIN clubs away ON away.id = f.away_club_id
         LEFT JOIN users verifier ON verifier.id = plc.verified_by_user_id
         WHERE plc.player_id = :player_id
         ORDER BY plc.verified_at DESC
         LIMIT 8',
        ['player_id' => $playerId]
    );
}

function license_verification_fixture_options(): array
{
    if (!player_license_tables_ready()) {
        return [];
    }

    return db_fetch_all(
        'SELECT f.id,
                f.match_date,
                f.match_time,
                f.status,
                c.name AS competition_name,
                home.name AS home_club_name,
                away.name AS away_club_name,
                v.name AS venue_name
         FROM fixtures f
         INNER JOIN competitions c ON c.id = f.competition_id
         INNER JOIN clubs home ON home.id = f.home_club_id
         INNER JOIN clubs away ON away.id = f.away_club_id
         LEFT JOIN venues v ON v.id = f.venue_id
         WHERE (
                f.status = "live"
                OR (
                    f.status = "scheduled"
                    AND f.match_date IS NOT NULL
                    AND f.match_date >= CURDATE()
                )
         )
         ORDER BY f.match_date ASC, f.match_time ASC, home.name ASC'
    );
}

function fixture_license_verification_is_open(array $fixture): bool
{
    if (($fixture['status'] ?? '') !== 'scheduled') {
        return false;
    }

    $matchDate = trim((string) ($fixture['match_date'] ?? ''));
    $matchTime = trim((string) ($fixture['match_time'] ?? ''));

    if ($matchDate === '' || $matchTime === '') {
        return false;
    }

    $kickoffTimestamp = strtotime($matchDate . ' ' . $matchTime);

    return $kickoffTimestamp !== false && $kickoffTimestamp > time();
}

function player_license_fixture_players(int $fixtureId): array
{
    if (!player_license_tables_ready()) {
        return [];
    }

    $fixture = find_fixture_or_abort($fixtureId);

    return db_fetch_all(
        'SELECT p.id AS player_id,
                p.first_name,
                p.last_name,
                p.registration_number,
                p.position,
                p.jersey_number,
                p.eligibility_status,
                p.status AS player_status,
                c.id AS club_id,
                c.name AS club_name,
                pl.id AS license_id,
                pl.license_number,
                pl.verification_code,
                pl.status AS license_status,
                pl.expires_at,
                plc.verified_at,
                verifier.full_name AS verified_by_name
         FROM players p
         INNER JOIN clubs c ON c.id = p.club_id
         LEFT JOIN player_licenses pl ON pl.player_id = p.id
         LEFT JOIN player_license_checks plc ON plc.fixture_id = :fixture_id_checks AND plc.player_id = p.id
         LEFT JOIN users verifier ON verifier.id = plc.verified_by_user_id
         WHERE p.status = "active"
           AND p.club_id IN (:home_club_id_players, :away_club_id_players)
           AND p.eligibility_status IN ("eligible", "suspended")
         ORDER BY CASE WHEN p.club_id = :home_club_id_sort THEN 0 ELSE 1 END,
                  COALESCE(p.jersey_number, 999),
                  p.last_name ASC,
                  p.first_name ASC',
        [
            'fixture_id_checks' => $fixtureId,
            'home_club_id_players' => $fixture['home_club_id'],
            'away_club_id_players' => $fixture['away_club_id'],
            'home_club_id_sort' => $fixture['home_club_id'],
        ]
    );
}

function player_license_player_belongs_to_fixture(int $playerId, array $fixture): bool
{
    $playerRecord = find_player_or_abort($playerId);

    return in_array((int) $playerRecord['club_id'], [(int) $fixture['home_club_id'], (int) $fixture['away_club_id']], true);
}

function verify_player_license_for_fixture(int $fixtureId, int $playerId, int $licenseId, int $verifiedByUserId): void
{
    db_query(
        'INSERT INTO player_license_checks (fixture_id, player_id, license_id, verified_by_user_id, verified_at, notes, created_at, updated_at)
         VALUES (:fixture_id, :player_id, :license_id, :verified_by_user_id, NOW(), NULL, NOW(), NOW())
         ON DUPLICATE KEY UPDATE
             license_id = VALUES(license_id),
             verified_by_user_id = VALUES(verified_by_user_id),
             verified_at = VALUES(verified_at),
             updated_at = VALUES(updated_at)',
        [
            'fixture_id' => $fixtureId,
            'player_id' => $playerId,
            'license_id' => $licenseId,
            'verified_by_user_id' => $verifiedByUserId,
        ]
    );
}

function clear_player_license_verification(int $fixtureId, int $playerId): void
{
    db_query(
        'DELETE FROM player_license_checks
         WHERE fixture_id = :fixture_id
           AND player_id = :player_id',
        [
            'fixture_id' => $fixtureId,
            'player_id' => $playerId,
        ]
    );
}
