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

function finance_module_boot(): void
{
    require_login();

    if (!can_manage_finance() && !can_record_payments()) {
        abort_request(403, 'Access denied', 'You do not have permission to access finance management.');
    }
}

function can_manage_finance(): bool
{
    return can('manage_finance');
}

function can_record_payments(): bool
{
    return can('record_payments');
}

function payment_type_options(): array
{
    return ['registration', 'fine', 'appeal_fee', 'other'];
}

function payment_status_options(): array
{
    return ['unpaid', 'partial', 'paid'];
}

function payment_type_label(string $type): string
{
    return match ($type) {
        'registration' => 'Registration',
        'fine' => 'Fine',
        'appeal_fee' => 'Appeal fee',
        'other' => 'Other',
        default => ucfirst(str_replace('_', ' ', $type)),
    };
}

function payment_status_badge_class(string $status): string
{
    return match ($status) {
        'paid' => 'badge-soft-success',
        'partial' => 'bg-warning-subtle text-warning-emphasis',
        'unpaid' => 'badge-soft-danger',
        default => 'text-bg-light',
    };
}

function finance_payment_list(?int $clubIdFilter = null): array
{
    $sql = 'SELECT cp.*, c.name AS club_name, c.short_name AS club_short_name,
                   s.name AS season_name, s.status AS season_status
            FROM club_payments cp
            INNER JOIN clubs c ON c.id = cp.club_id
            INNER JOIN seasons s ON s.id = cp.season_id';
    $conditions = [];
    $params = [];

    if ($clubIdFilter !== null && $clubIdFilter > 0) {
        if (get_club_by_id($clubIdFilter) === null) {
            return [];
        }

        if (is_club_admin()) {
            require_club_scope($clubIdFilter);
        }

        $conditions[] = 'cp.club_id = :club_filter';
        $params['club_filter'] = $clubIdFilter;
    }

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

        $conditions[] = 'cp.club_id = :club_id';
        $params['club_id'] = $clubId;
    }

    if ($conditions !== []) {
        $sql .= ' WHERE ' . implode(' AND ', $conditions);
    }

    $sql .= ' ORDER BY COALESCE(cp.payment_date, "9999-12-31") DESC, cp.created_at DESC, cp.id DESC';

    return db_fetch_all($sql, $params);
}

function finance_summary_cards(?int $clubIdFilter = null): array
{
    $conditions = [];
    $params = [];

    if ($clubIdFilter !== null && $clubIdFilter > 0) {
        if (get_club_by_id($clubIdFilter) === null) {
            return [
                'total_due' => 0.0,
                'total_paid' => 0.0,
                'outstanding' => 0.0,
                'unpaid_count' => 0,
            ];
        }

        if (is_club_admin()) {
            require_club_scope($clubIdFilter);
        }

        $conditions[] = 'club_id = :club_filter';
        $params['club_filter'] = $clubIdFilter;
    }

    if (is_club_admin()) {
        $clubId = user_club_id();
        if ($clubId === null) {
            return [
                'total_due' => 0.0,
                'total_paid' => 0.0,
                'outstanding' => 0.0,
                'unpaid_count' => 0,
            ];
        }

        $conditions[] = 'club_id = :club_id';
        $params['club_id'] = $clubId;
    }

    $where = $conditions === [] ? '' : ' WHERE ' . implode(' AND ', $conditions);

    return [
        'total_due' => (float) db_fetch_value('SELECT COALESCE(SUM(amount), 0) FROM club_payments' . $where, $params, 0),
        'total_paid' => (float) db_fetch_value('SELECT COALESCE(SUM(amount_paid), 0) FROM club_payments' . $where, $params, 0),
        'outstanding' => (float) db_fetch_value('SELECT COALESCE(SUM(amount - amount_paid), 0) FROM club_payments' . $where, $params, 0),
        'unpaid_count' => (int) db_fetch_value('SELECT COUNT(*) FROM club_payments' . ($where === '' ? ' WHERE payment_status <> "paid"' : $where . ' AND payment_status <> "paid"'), $params, 0),
    ];
}

function get_finance_payment_by_id(int $paymentId): ?array
{
    $payment = db_fetch_one(
        'SELECT cp.*, c.name AS club_name, c.short_name AS club_short_name,
                s.name AS season_name, s.status AS season_status
         FROM club_payments cp
         INNER JOIN clubs c ON c.id = cp.club_id
         INNER JOIN seasons s ON s.id = cp.season_id
         WHERE cp.id = :id
         LIMIT 1',
        ['id' => $paymentId]
    );

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

    return $payment;
}

function find_finance_payment_or_abort(int $paymentId): array
{
    $payment = get_finance_payment_by_id($paymentId);

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

    return $payment;
}

function finance_status_from_amounts(float $amount, float $amountPaid): string
{
    if ($amountPaid <= 0.0) {
        return 'unpaid';
    }

    if ($amountPaid >= $amount) {
        return 'paid';
    }

    return 'partial';
}

function validate_finance_payment_payload(array $data, array &$errors, ?int $paymentId = null): array
{
    validate_required($data, [
        'club_id' => 'Club',
        'season_id' => 'Season',
        'payment_type' => 'Payment type',
        'amount' => 'Amount due',
        'payment_reference' => 'Reference',
    ], $errors);

    validate_in_array_field($data, 'payment_type', payment_type_options(), 'Payment type', $errors);
    validate_numeric_min($data, 'amount', 'Amount due', 0.01, $errors);
    validate_numeric_min($data, 'amount_paid', 'Amount paid', 0, $errors);
    validate_date_field($data, 'payment_date', 'Payment date', $errors);

    $normalized = [
        'club_id' => (int) ($data['club_id'] ?? 0),
        'season_id' => (int) ($data['season_id'] ?? 0),
        'payment_type' => (string) ($data['payment_type'] ?? 'registration'),
        'amount' => round((float) ($data['amount'] ?? 0), 2),
        'amount_paid' => round((float) ($data['amount_paid'] ?? 0), 2),
        'payment_reference' => trim((string) ($data['payment_reference'] ?? '')),
        'payment_date' => trim((string) ($data['payment_date'] ?? '')),
    ];

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

    if ($normalized['season_id'] <= 0 || get_season_by_id($normalized['season_id']) === null) {
        $errors['season_id'] = 'Selected season is invalid.';
    }

    if ($normalized['amount_paid'] > $normalized['amount'] && $normalized['amount'] > 0) {
        $errors['amount_paid'] = 'Amount paid cannot exceed the amount due.';
    }

    if (duplicate_exists('club_payments', 'payment_reference', $normalized['payment_reference'], $paymentId) && $normalized['payment_reference'] !== '') {
        $errors['payment_reference'] = 'Payment reference already exists.';
    }

    $normalized['payment_status'] = finance_status_from_amounts($normalized['amount'], $normalized['amount_paid']);
    $normalized['payment_date'] = $normalized['amount_paid'] > 0 && $normalized['payment_date'] === ''
        ? date('Y-m-d')
        : ($normalized['payment_date'] !== '' ? $normalized['payment_date'] : null);

    return $normalized;
}

function validate_finance_record_payload(array $data, array $existingPayment, array &$errors): array
{
    validate_required($data, ['amount_paid' => 'Amount paid'], $errors);
    validate_numeric_min($data, 'amount_paid', 'Amount paid', 0, $errors);
    validate_date_field($data, 'payment_date', 'Payment date', $errors);

    $amountPaid = round((float) ($data['amount_paid'] ?? 0), 2);
    if ($amountPaid > (float) $existingPayment['amount']) {
        $errors['amount_paid'] = 'Amount paid cannot exceed the amount due.';
    }

    return [
        'amount_paid' => $amountPaid,
        'payment_date' => trim((string) ($data['payment_date'] ?? '')) !== '' ? trim((string) ($data['payment_date'] ?? '')) : ($amountPaid > 0 ? date('Y-m-d') : null),
        'payment_status' => finance_status_from_amounts((float) $existingPayment['amount'], $amountPaid),
    ];
}
