<?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';

function protest_module_boot(): void
{
    require_login();

    if (!can_manage_protests() && !can_decide_protests()) {
        abort_request(403, 'Access denied', 'You do not have permission to access protests management.');
    }
}

function can_manage_protests(): bool
{
    return can('manage_protests');
}

function can_decide_protests(): bool
{
    return can('decide_protests');
}

function protest_status_options(): array
{
    return ['submitted', 'under_review', 'accepted', 'rejected', 'resolved'];
}

function protest_status_badge_class(string $status): string
{
    return match ($status) {
        'submitted' => 'text-bg-light',
        'under_review' => 'bg-info-subtle text-info-emphasis',
        'accepted', 'resolved' => 'badge-soft-success',
        'rejected' => 'badge-soft-danger',
        default => 'text-bg-light',
    };
}

function protest_list(): array
{
    $sql = 'SELECT p.*, f.matchday, f.match_date,
                   pc.name AS protesting_club_name, ac.name AS against_club_name,
                   c.name AS competition_name, s.name AS season_name
            FROM protests p
            INNER JOIN fixtures f ON f.id = p.fixture_id
            INNER JOIN clubs pc ON pc.id = p.protesting_club_id
            INNER JOIN clubs ac ON ac.id = p.against_club_id
            INNER JOIN competitions c ON c.id = f.competition_id
            INNER JOIN seasons s ON s.id = f.season_id';
    $params = [];

    if (is_club_admin()) {
        $clubId = user_club_id();
        if ($clubId === null) {
            return [];
        }

        $sql .= ' WHERE p.protesting_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 protest_fixture_options(): array
{
    $sql = 'SELECT f.id, f.matchday, f.match_date,
                   hc.id AS home_club_id, hc.name AS home_club_name,
                   ac.id AS away_club_id, ac.name AS away_club_name,
                   c.name AS competition_name, s.name AS season_name
            FROM fixtures f
            INNER JOIN clubs hc ON hc.id = f.home_club_id
            INNER JOIN clubs ac ON ac.id = f.away_club_id
            INNER JOIN competitions c ON c.id = f.competition_id
            INNER JOIN seasons s ON s.id = f.season_id
            INNER JOIN match_results mr ON mr.fixture_id = f.id
            WHERE mr.result_status IN ("submitted", "verified")';
    $params = [];

    if (is_club_admin()) {
        $clubId = user_club_id();
        if ($clubId === null) {
            return [];
        }

        $sql .= ' AND (f.home_club_id = :club_id OR f.away_club_id = :club_id)';
        $params['club_id'] = $clubId;
    }

    $sql .= ' ORDER BY COALESCE(f.match_date, "9999-12-31") DESC, f.id DESC';

    return db_fetch_all($sql, $params);
}

function get_protest_by_id(int $protestId): ?array
{
    $protest = db_fetch_one(
        'SELECT p.*, f.matchday, f.match_date, f.home_club_id, f.away_club_id,
                pc.name AS protesting_club_name, ac.name AS against_club_name,
                c.name AS competition_name, s.name AS season_name
         FROM protests p
         INNER JOIN fixtures f ON f.id = p.fixture_id
         INNER JOIN clubs pc ON pc.id = p.protesting_club_id
         INNER JOIN clubs ac ON ac.id = p.against_club_id
         INNER JOIN competitions c ON c.id = f.competition_id
         INNER JOIN seasons s ON s.id = f.season_id
         WHERE p.id = :id
         LIMIT 1',
        ['id' => $protestId]
    );

    if ($protest !== null && is_club_admin()) {
        require_club_scope((int) $protest['protesting_club_id']);
    }

    return $protest;
}

function find_protest_or_abort(int $protestId): array
{
    $protest = get_protest_by_id($protestId);

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

    return $protest;
}

function protest_fixture_by_id(int $fixtureId): ?array
{
    return db_fetch_one(
        'SELECT f.id, f.home_club_id, f.away_club_id, f.competition_id, f.season_id,
                hc.name AS home_club_name, ac.name AS away_club_name,
                mr.result_status
         FROM fixtures f
         INNER JOIN clubs hc ON hc.id = f.home_club_id
         INNER JOIN clubs ac ON ac.id = f.away_club_id
         LEFT JOIN match_results mr ON mr.fixture_id = f.id
         WHERE f.id = :id
         LIMIT 1',
        ['id' => $fixtureId]
    );
}

function protest_fixture_club_options(?int $fixtureId): array
{
    if ($fixtureId === null || $fixtureId <= 0) {
        return [];
    }

    $fixture = protest_fixture_by_id($fixtureId);
    if ($fixture === null) {
        return [];
    }

    return [
        ['id' => (int) $fixture['home_club_id'], 'name' => (string) $fixture['home_club_name']],
        ['id' => (int) $fixture['away_club_id'], 'name' => (string) $fixture['away_club_name']],
    ];
}

function protest_has_dependencies(int $protestId): bool
{
    return false;
}

function validate_protest_payload(array $data, array &$errors, ?int $protestId = null): array
{
    validate_required($data, [
        'fixture_id' => 'Fixture',
        'protesting_club_id' => 'Protesting club',
        'against_club_id' => 'Against club',
        'title' => 'Title',
        'complaint' => 'Complaint',
    ], $errors);

    if (!is_club_admin()) {
        validate_in_array_field($data, 'status', protest_status_options(), 'Status', $errors);
    }

    $normalized = [
        'fixture_id' => (int) ($data['fixture_id'] ?? 0),
        'protesting_club_id' => is_club_admin() ? (int) user_club_id() : (int) ($data['protesting_club_id'] ?? 0),
        'against_club_id' => (int) ($data['against_club_id'] ?? 0),
        'title' => trim((string) ($data['title'] ?? '')),
        'complaint' => trim((string) ($data['complaint'] ?? '')),
        'status' => is_club_admin() ? 'submitted' : (string) ($data['status'] ?? 'submitted'),
        'decision' => can_decide_protests() ? trim((string) ($data['decision'] ?? '')) : '',
    ];

    $fixture = protest_fixture_by_id($normalized['fixture_id']);
    if ($fixture === null) {
        $errors['fixture_id'] = 'Selected fixture is invalid.';
        return $normalized;
    }

    if (!in_array($normalized['protesting_club_id'], [(int) $fixture['home_club_id'], (int) $fixture['away_club_id']], true)) {
        $errors['protesting_club_id'] = 'Protesting club must belong to the selected fixture.';
    }

    if (!in_array($normalized['against_club_id'], [(int) $fixture['home_club_id'], (int) $fixture['away_club_id']], true)) {
        $errors['against_club_id'] = 'Against club must belong to the selected fixture.';
    }

    if ($normalized['protesting_club_id'] > 0 && $normalized['protesting_club_id'] === $normalized['against_club_id']) {
        $errors['against_club_id'] = 'Against club must be different from the protesting club.';
    }

    if (is_club_admin()) {
        require_club_scope($normalized['protesting_club_id']);
    }

    $query = 'SELECT COUNT(*) FROM protests WHERE fixture_id = :fixture_id AND protesting_club_id = :protesting_club_id AND title = :title';
    $params = [
        'fixture_id' => $normalized['fixture_id'],
        'protesting_club_id' => $normalized['protesting_club_id'],
        'title' => $normalized['title'],
    ];

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

    if ($normalized['title'] !== '' && (int) db_fetch_value($query, $params, 0) > 0) {
        $errors['title'] = 'A protest with this title already exists for the selected fixture and club.';
    }

    return $normalized;
}

function validate_protest_decision_payload(array $data, array &$errors): array
{
    validate_required($data, ['status' => 'Status'], $errors);
    validate_in_array_field($data, 'status', protest_status_options(), 'Status', $errors);

    return [
        'status' => (string) ($data['status'] ?? 'under_review'),
        'decision' => trim((string) ($data['decision'] ?? '')),
        'resolved_at' => in_array((string) ($data['status'] ?? ''), ['accepted', 'rejected', 'resolved'], true) ? date('Y-m-d H:i:s') : null,
    ];
}
