<?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 dirname(__DIR__, 2) . '/core/upload.php';
require_once dirname(__DIR__, 2) . '/modules/clubs/helpers.php';

function player_module_boot(): void
{
    require_login();
    require_permission('manage_players');
}

function player_position_options(): array
{
    return ['GK', 'CB', 'LB', 'RB', 'DM', 'CM', 'AM', 'LW', 'RW', 'ST'];
}

function player_medical_options(): array
{
    return ['pending', 'fit', 'unfit'];
}

function player_eligibility_options(): array
{
    return ['pending', 'eligible', 'ineligible', 'suspended'];
}

function player_transfer_options(): array
{
    return ['active', 'free_agent', 'transferred', 'released'];
}

function player_status_options(): array
{
    return ['active', 'inactive'];
}

function player_document_type_options(): array
{
    return ['id_card', 'passport', 'medical_certificate', 'clearance_letter', 'contract', 'other'];
}

function can_manage_player_approval(): bool
{
    return !is_club_admin() && can('approve_players');
}

function player_visible_club_id(): ?int
{
    if (is_club_admin()) {
        return user_club_id();
    }

    $clubId = isset($_GET['club_id']) ? (int) $_GET['club_id'] : 0;
    return $clubId > 0 ? $clubId : null;
}

function player_list(): array
{
    $clubId = player_visible_club_id();
    $params = [];
    $sql = 'SELECT p.*, c.name AS club_name, c.short_name,
                   (SELECT COUNT(*) FROM player_documents pd WHERE pd.player_id = p.id) AS documents_count
            FROM players p
            INNER JOIN clubs c ON c.id = p.club_id';

    if ($clubId !== null) {
        require_club_scope($clubId);
        $sql .= ' WHERE p.club_id = :club_id';
        $params['club_id'] = $clubId;
    }

    $sql .= ' ORDER BY p.created_at DESC, p.id DESC';

    return db_fetch_all($sql, $params);
}

function player_club_options(): array
{
    if (is_club_admin()) {
        $clubId = user_club_id();
        $club = $clubId !== null ? get_club_by_id((int) $clubId) : null;
        return $club ? [$club] : [];
    }

    return db_fetch_all('SELECT id, name, short_name, status, license_status FROM clubs ORDER BY name ASC');
}

function get_player_by_id(int $playerId): ?array
{
    $player = db_fetch_one(
        'SELECT p.*, c.name AS club_name, c.short_name, c.logo AS club_logo, c.license_status AS club_license_status, c.status AS club_status,
                (SELECT COUNT(*) FROM player_documents pd WHERE pd.player_id = p.id) AS documents_count
         FROM players p
         INNER JOIN clubs c ON c.id = p.club_id
         WHERE p.id = :id
         LIMIT 1',
        ['id' => $playerId]
    );

    if ($player !== null) {
        require_club_scope((int) $player['club_id']);
    }

    return $player;
}

function find_player_or_abort(int $playerId): array
{
    $player = get_player_by_id($playerId);

    if ($player === null) {
        abort_request(404, 'Player not found', 'The requested player record does not exist.');
    }

    return $player;
}

function player_documents(int $playerId): array
{
    require_club_scope((int) (get_player_by_id($playerId)['club_id'] ?? null));

    return db_fetch_all(
        'SELECT *
         FROM player_documents
         WHERE player_id = :player_id
         ORDER BY uploaded_at DESC, id DESC',
        ['player_id' => $playerId]
    );
}

function get_player_document_by_id(int $documentId): ?array
{
    $document = db_fetch_one(
        'SELECT pd.*, p.club_id, p.first_name, p.last_name
         FROM player_documents pd
         INNER JOIN players p ON p.id = pd.player_id
         WHERE pd.id = :id
         LIMIT 1',
        ['id' => $documentId]
    );

    if ($document !== null) {
        require_club_scope((int) $document['club_id']);
    }

    return $document;
}

function find_player_document_or_abort(int $documentId): array
{
    $document = get_player_document_by_id($documentId);

    if ($document === null) {
        abort_request(404, 'Document not found', 'The requested player document does not exist.');
    }

    return $document;
}

function player_has_dependencies(int $playerId): bool
{
    return (int) db_fetch_value(
        'SELECT (
            (SELECT COUNT(*) FROM match_events WHERE player_id = :player_id OR related_player_id = :player_id) +
            (SELECT COUNT(*) FROM suspensions WHERE player_id = :player_id) +
            (SELECT COUNT(*) FROM player_statistics WHERE player_id = :player_id) +
            (SELECT COUNT(*) FROM disciplinary_cases WHERE player_id = :player_id) +
            (SELECT COUNT(*) FROM awards WHERE player_id = :player_id)
        )',
        ['player_id' => $playerId],
        0
    ) > 0;
}

function validate_player_payload(array $data, array &$errors, ?int $playerId = null, ?array $existingPlayer = null): array
{
    validate_required($data, [
        'first_name' => 'First name',
        'last_name' => 'Last name',
        'date_of_birth' => 'Date of birth',
        'nin' => 'NIN',
        'position' => 'Position',
        'status' => 'Status',
    ], $errors);
    validate_date_field($data, 'date_of_birth', 'Date of birth', $errors);
    validate_in_array_field($data, 'position', player_position_options(), 'Position', $errors);
    validate_in_array_field($data, 'medical_status', player_medical_options(), 'Medical status', $errors);
    validate_in_array_field($data, 'transfer_status', player_transfer_options(), 'Transfer status', $errors);
    validate_in_array_field($data, 'status', player_status_options(), 'Status', $errors);

    if (can_manage_player_approval()) {
        validate_in_array_field($data, 'eligibility_status', player_eligibility_options(), 'Eligibility status', $errors);
    }

    $clubId = is_club_admin() ? (int) user_club_id() : (int) ($data['club_id'] ?? 0);
    $club = $clubId > 0 ? get_club_by_id($clubId) : null;

    if ($clubId <= 0 || $club === null) {
        $errors['club_id'] = 'Selected club is invalid.';
    } else {
        require_club_scope($clubId);

        if (trim((string) ($club['short_name'] ?? '')) === '') {
            $errors['club_id'] = 'Selected club must have a unique short name before players can be registered.';
        }
    }

    $dateOfBirth = trim((string) ($data['date_of_birth'] ?? ''));
    if ($dateOfBirth !== '') {
        $timestamp = strtotime($dateOfBirth);
        if ($timestamp === false || $timestamp > time()) {
            $errors['date_of_birth'] = 'Date of birth cannot be in the future.';
        }
    }

    $jerseyNumber = trim((string) ($data['jersey_number'] ?? ''));
    if ($jerseyNumber !== '' && (!ctype_digit($jerseyNumber) || (int) $jerseyNumber < 0 || (int) $jerseyNumber > 99)) {
        $errors['jersey_number'] = 'Jersey number must be between 0 and 99.';
    }

    $nin = preg_replace('/\D+/', '', (string) ($data['nin'] ?? ''));
    if ($nin !== '' && !preg_match('/^\d{11}$/', $nin)) {
        $errors['nin'] = 'NIN must be exactly 11 digits.';
    }

    $normalized = [
        'club_id' => $clubId,
        'first_name' => trim((string) ($data['first_name'] ?? '')),
        'last_name' => trim((string) ($data['last_name'] ?? '')),
        'date_of_birth' => $dateOfBirth,
        'nationality' => trim((string) ($data['nationality'] ?? '')),
        'position' => (string) ($data['position'] ?? 'GK'),
        'jersey_number' => $jerseyNumber === '' ? null : (int) $jerseyNumber,
        'nin' => $nin,
        'registration_number' => (string) ($existingPlayer['registration_number'] ?? ''),
        'medical_status' => (string) ($data['medical_status'] ?? ($existingPlayer['medical_status'] ?? 'pending')),
        'eligibility_status' => can_manage_player_approval()
            ? (string) ($data['eligibility_status'] ?? ($existingPlayer['eligibility_status'] ?? 'pending'))
            : (string) ($existingPlayer['eligibility_status'] ?? 'pending'),
        'transfer_status' => (string) ($data['transfer_status'] ?? ($existingPlayer['transfer_status'] ?? 'active')),
        'status' => (string) ($data['status'] ?? ($existingPlayer['status'] ?? 'active')),
    ];

    if ($normalized['nin'] !== '' && duplicate_exists('players', 'nin', $normalized['nin'], $playerId)) {
        $errors['nin'] = 'NIN already exists.';
    }

    if ($normalized['jersey_number'] !== null) {
        $duplicateJersey = db_fetch_value(
            'SELECT COUNT(*) FROM players WHERE club_id = :club_id AND jersey_number = :jersey_number' . ($playerId !== null ? ' AND id <> :id' : ''),
            $playerId !== null
                ? ['club_id' => $clubId, 'jersey_number' => $normalized['jersey_number'], 'id' => $playerId]
                : ['club_id' => $clubId, 'jersey_number' => $normalized['jersey_number']],
            0
        );

        if ((int) $duplicateJersey > 0) {
            $errors['jersey_number'] = 'Jersey number is already assigned in this club.';
        }
    }

    return $normalized;
}

function player_generate_registration_number(int $clubId, ?int $ignorePlayerId = null, ?string $currentRegistrationNumber = null): string
{
    $club = get_club_by_id($clubId);
    if ($club === null) {
        throw new RuntimeException('Selected club is invalid.');
    }

    $prefix = strtoupper(trim((string) ($club['short_name'] ?? '')));
    if ($prefix === '') {
        throw new RuntimeException('Selected club must have a unique short name before player IDs can be generated.');
    }

    $normalizedCurrent = strtoupper(trim((string) $currentRegistrationNumber));
    if ($normalizedCurrent !== '' && preg_match('/^' . preg_quote($prefix, '/') . '\d+$/', $normalizedCurrent) === 1) {
        return $normalizedCurrent;
    }

    $params = ['club_id' => $clubId];
    $sql = 'SELECT registration_number FROM players WHERE club_id = :club_id';

    if ($ignorePlayerId !== null) {
        $sql .= ' AND id <> :id';
        $params['id'] = $ignorePlayerId;
    }

    $existingRows = db_fetch_all($sql, $params);
    $maxSequence = 0;

    foreach ($existingRows as $row) {
        $registrationNumber = strtoupper(trim((string) ($row['registration_number'] ?? '')));
        if (preg_match('/^' . preg_quote($prefix, '/') . '(\d+)$/', $registrationNumber, $matches) === 1) {
            $maxSequence = max($maxSequence, (int) $matches[1]);
        }
    }

    return $prefix . str_pad((string) ($maxSequence + 1), 3, '0', STR_PAD_LEFT);
}

function validate_player_document_payload(array $data, array $file, array &$errors): array
{
    validate_required($data, [
        'document_type' => 'Document type',
        'title' => 'Document title',
    ], $errors);
    validate_in_array_field($data, 'document_type', player_document_type_options(), 'Document type', $errors);

    if (($file['error'] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_NO_FILE) {
        $errors['document_file'] = 'Document file is required.';
    }

    return [
        'document_type' => (string) ($data['document_type'] ?? 'other'),
        'title' => trim((string) ($data['title'] ?? '')),
    ];
}

function player_badge_class(string $status): string
{
    return match ($status) {
        'active', 'eligible', 'fit' => 'badge-soft-success',
        'pending', 'free_agent', 'inactive' => 'text-bg-light',
        'ineligible', 'suspended', 'unfit', 'transferred', 'released' => 'badge-soft-danger',
        default => 'text-bg-light',
    };
}

function player_full_name(array $player): string
{
    return trim(($player['first_name'] ?? '') . ' ' . ($player['last_name'] ?? ''));
}

function player_bulk_template_headers(): array
{
    return [
        'first_name',
        'last_name',
        'date_of_birth',
        'nationality',
        'nin',
        'position',
        'jersey_number',
        'medical_status',
        'transfer_status',
        'status',
        'eligibility_status',
    ];
}

function player_bulk_template_sample_row(): array
{
    return [
        'first_name' => 'John',
        'last_name' => 'Doe',
        'date_of_birth' => '2004-01-15',
        'nationality' => 'Nigeria',
        'nin' => '12345678901',
        'position' => 'CM',
        'jersey_number' => '8',
        'medical_status' => 'pending',
        'transfer_status' => 'active',
        'status' => 'active',
        'eligibility_status' => 'pending',
    ];
}

function create_player_record(array $payload, ?string $photoPath = null): int
{
    db_query(
        'INSERT INTO players (club_id, first_name, last_name, date_of_birth, nationality, position, jersey_number, photo, nin, registration_number, medical_status, eligibility_status, transfer_status, status, created_at, updated_at)
         VALUES (:club_id, :first_name, :last_name, :date_of_birth, :nationality, :position, :jersey_number, :photo, :nin, :registration_number, :medical_status, :eligibility_status, :transfer_status, :status, NOW(), NOW())',
        $payload + ['photo' => $photoPath]
    );

    return (int) db()->lastInsertId();
}

function player_normalize_bulk_csv_header(string $header): string
{
    $header = preg_replace('/^\xEF\xBB\xBF/', '', $header) ?? $header;
    return strtolower(trim($header));
}

function player_parse_bulk_csv_upload(array $file, array &$errors): array
{
    if (($file['error'] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_NO_FILE) {
        $errors['bulk_file'] = 'Select a CSV file to import players.';
        return [];
    }

    if (($file['error'] ?? UPLOAD_ERR_OK) !== UPLOAD_ERR_OK) {
        $errors['bulk_file'] = 'The CSV upload could not be processed.';
        return [];
    }

    $extension = strtolower(pathinfo((string) ($file['name'] ?? ''), PATHINFO_EXTENSION));
    if ($extension !== 'csv') {
        $errors['bulk_file'] = 'Only CSV files are supported for bulk player registration.';
        return [];
    }

    $handle = fopen((string) $file['tmp_name'], 'rb');
    if ($handle === false) {
        $errors['bulk_file'] = 'The uploaded CSV file could not be opened.';
        return [];
    }

    $headerRow = fgetcsv($handle);
    if (!is_array($headerRow)) {
        fclose($handle);
        $errors['bulk_file'] = 'The CSV file is empty.';
        return [];
    }

    $expectedHeaders = player_bulk_template_headers();
    $normalizedHeaders = array_map('player_normalize_bulk_csv_header', $headerRow);
    if ($normalizedHeaders !== $expectedHeaders) {
        fclose($handle);
        $errors['bulk_file'] = 'The CSV columns do not match the player bulk template. Download the latest template and try again.';
        return [];
    }

    $rows = [];
    $lineNumber = 1;

    while (($data = fgetcsv($handle)) !== false) {
        $lineNumber++;

        if ($data === [null] || count(array_filter($data, static fn($value): bool => trim((string) $value) !== '')) === 0) {
            continue;
        }

        if (count($data) > count($expectedHeaders)) {
            fclose($handle);
            $errors['bulk_file'] = 'Row ' . $lineNumber . ' has more columns than the template allows.';
            return [];
        }

        $rows[] = [
            'line' => $lineNumber,
            'data' => array_combine($expectedHeaders, array_pad($data, count($expectedHeaders), '')),
        ];
    }

    fclose($handle);

    if ($rows === []) {
        $errors['bulk_file'] = 'The CSV file does not contain any player rows.';
    }

    return $rows;
}
