<?php
declare(strict_types=1);

namespace App\Modules\SportsActivity\Services;

use App\Core\App;
use App\Core\EventBus;

final class GateAccessService
{
    public static function checkAccess(string $scannedData): array
    {
        $db = App::getInstance()->db();

        $cardNumber = self::extractCardNumber($scannedData);
        if ($cardNumber === null) {
            return ['granted' => false, 'reason' => 'بيانات المسح غير صالحة'];
        }

        $card = $db->selectOne(
            "SELECT c.*, p.full_name_ar, p.photo_path, p.player_type
             FROM sa_player_cards c
             INNER JOIN sa_players p ON p.id = c.player_id
             WHERE c.card_number = ? AND c.is_archived = 0",
            [$cardNumber]
        );

        if (!$card) {
            return ['granted' => false, 'reason' => 'الكارت غير موجود في النظام'];
        }

        if ($card['status'] === 'suspended') {
            self::dispatchDenialEvent($cardNumber, $card, 'الكارت موقوف');
            return [
                'granted' => false,
                'reason'  => 'الكارت موقوف',
                'card'    => $card,
            ];
        }

        if ($card['status'] === 'revoked') {
            self::dispatchDenialEvent($cardNumber, $card, 'الكارت ملغى');
            return [
                'granted' => false,
                'reason'  => 'الكارت ملغى',
                'card'    => $card,
            ];
        }

        if ($card['status'] === 'expired') {
            self::dispatchDenialEvent($cardNumber, $card, 'الكارت منتهي الصلاحية');
            return [
                'granted' => false,
                'reason'  => 'الكارت منتهي الصلاحية',
                'card'    => $card,
            ];
        }

        if ($card['valid_until'] !== null && $card['valid_until'] < date('Y-m-d')) {
            $db->update('sa_player_cards', [
                'status'     => 'expired',
                'updated_at' => date('Y-m-d H:i:s'),
            ], 'id = ?', [(int) $card['id']]);

            self::dispatchDenialEvent($cardNumber, $card, 'الكارت منتهي الصلاحية');
            return [
                'granted' => false,
                'reason'  => 'الكارت منتهي الصلاحية',
                'card'    => $card,
            ];
        }

        if (!in_array($card['status'], ['active', 'temporary'], true)) {
            $result = [
                'granted' => false,
                'reason'  => 'حالة الكارت غير صالحة للدخول (' . $card['status'] . ')',
                'card'    => $card,
            ];
            self::dispatchDenialEvent($cardNumber, $card, $result['reason']);
            return $result;
        }

        return [
            'granted'     => true,
            'card'        => $card,
            'player_name' => $card['full_name_ar'],
            'player_type' => $card['player_type'],
            'card_type'   => $card['card_type'],
            'valid_until' => $card['valid_until'],
        ];
    }

    private static function dispatchDenialEvent(?string $cardNumber, ?array $card, string $reason): void
    {
        EventBus::dispatch('sa.gate.access_denied', [
            'player_id'   => (int) ($card['player_id'] ?? 0),
            'player_name' => $card['full_name_ar'] ?? '',
            'card_number' => $cardNumber ?? '',
            'reason'      => $reason,
        ]);
    }

    public static function recordEntry(int $playerId, ?int $cardId, string $accessPoint = ''): int
    {
        $db = App::getInstance()->db();
        $employee = App::getInstance()->currentEmployee();

        return $db->insert('sa_gate_access_log', [
            'player_id'    => $playerId,
            'card_id'      => $cardId,
            'access_type'  => 'entry',
            'access_point' => $accessPoint ?: null,
            'granted'      => 1,
            'recorded_by'  => $employee ? (int) $employee->id : null,
            'recorded_at'  => date('Y-m-d H:i:s'),
        ]);
    }

    public static function recordDenial(int $playerId, ?int $cardId, string $reason, string $scannedData = ''): int
    {
        $db = App::getInstance()->db();
        $employee = App::getInstance()->currentEmployee();

        return $db->insert('sa_gate_access_log', [
            'player_id'     => $playerId,
            'card_id'       => $cardId,
            'access_type'   => 'entry',
            'access_point'  => null,
            'granted'       => 0,
            'denial_reason' => $reason,
            'scanned_data'  => $scannedData ?: null,
            'recorded_by'   => $employee ? (int) $employee->id : null,
            'recorded_at'   => date('Y-m-d H:i:s'),
        ]);
    }

    public static function recordExit(int $playerId, string $accessPoint = ''): void
    {
        $db = App::getInstance()->db();
        $employee = App::getInstance()->currentEmployee();

        $db->insert('sa_gate_access_log', [
            'player_id'    => $playerId,
            'card_id'      => null,
            'access_type'  => 'exit',
            'access_point' => $accessPoint ?: null,
            'granted'      => 1,
            'recorded_by'  => $employee ? (int) $employee->id : null,
            'recorded_at'  => date('Y-m-d H:i:s'),
        ]);
    }

    public static function getPlayerAccessHistory(int $playerId, ?string $from = null, ?string $to = null): array
    {
        $db = App::getInstance()->db();
        $where = 'g.player_id = ?';
        $params = [$playerId];

        if ($from) {
            $where .= ' AND g.recorded_at >= ?';
            $params[] = $from . ' 00:00:00';
        }
        if ($to) {
            $where .= ' AND g.recorded_at <= ?';
            $params[] = $to . ' 23:59:59';
        }

        return $db->select(
            "SELECT g.*, e.full_name_ar as recorded_by_name
             FROM sa_gate_access_log g
             LEFT JOIN employees e ON e.id = g.recorded_by
             WHERE {$where}
             ORDER BY g.recorded_at DESC
             LIMIT 200",
            $params
        );
    }

    public static function getDailyAccessReport(?string $date = null): array
    {
        $db = App::getInstance()->db();
        $date = $date ?: date('Y-m-d');

        $summary = $db->selectOne(
            "SELECT
                COUNT(*) as total_scans,
                SUM(CASE WHEN granted = 1 AND access_type = 'entry' THEN 1 ELSE 0 END) as entries_granted,
                SUM(CASE WHEN granted = 0 THEN 1 ELSE 0 END) as entries_denied,
                SUM(CASE WHEN access_type = 'exit' THEN 1 ELSE 0 END) as exits,
                COUNT(DISTINCT player_id) as unique_players
             FROM sa_gate_access_log
             WHERE DATE(recorded_at) = ?",
            [$date]
        );

        $entries = $db->select(
            "SELECT g.*, p.full_name_ar, p.player_type, c.card_number, c.card_type
             FROM sa_gate_access_log g
             INNER JOIN sa_players p ON p.id = g.player_id
             LEFT JOIN sa_player_cards c ON c.id = g.card_id
             WHERE DATE(g.recorded_at) = ?
             ORDER BY g.recorded_at DESC
             LIMIT 500",
            [$date]
        );

        return [
            'date'    => $date,
            'summary' => $summary,
            'entries' => $entries,
        ];
    }

    public static function getPlayerSessionToday(int $playerId): array
    {
        $db = App::getInstance()->db();
        $today = date('Y-m-d');
        $dayOfWeek = (int) date('w');
        $now = date('H:i:s');

        $booking = $db->selectOne(
            "SELECT b.start_time, b.end_time, g.name_ar as group_name, fu.name_ar as unit_name
             FROM sa_bookings b
             INNER JOIN sa_groups g ON g.id = b.group_id
             INNER JOIN sa_facility_units fu ON fu.id = b.facility_unit_id
             INNER JOIN sa_group_players gp ON gp.group_id = b.group_id AND gp.player_id = ? AND gp.status = 'active'
             WHERE b.booking_date = ? AND b.status = 'confirmed' AND b.booking_type = 'training'
             AND b.end_time > ?
             ORDER BY b.start_time ASC
             LIMIT 1",
            [$playerId, $today, $now]
        );

        if ($booking) {
            return [
                'has_session_today' => true,
                'session_info' => [
                    'group_name' => $booking['group_name'],
                    'start_time' => substr($booking['start_time'], 0, 5),
                    'end_time'   => substr($booking['end_time'], 0, 5),
                    'unit_name'  => $booking['unit_name'],
                    'source'     => 'booking',
                ],
            ];
        }

        $schedule = $db->selectOne(
            "SELECT gs.start_time, gs.end_time, g.name_ar as group_name, fu.name_ar as unit_name
             FROM sa_group_schedule gs
             INNER JOIN sa_groups g ON g.id = gs.group_id
             INNER JOIN sa_facility_units fu ON fu.id = gs.facility_unit_id
             INNER JOIN sa_group_players gp ON gp.group_id = gs.group_id AND gp.player_id = ? AND gp.status = 'active'
             WHERE gs.day_of_week = ? AND gs.is_active = 1
             AND gs.end_time > ?
             ORDER BY gs.start_time ASC
             LIMIT 1",
            [$playerId, $dayOfWeek, $now]
        );

        if ($schedule) {
            return [
                'has_session_today' => true,
                'session_info' => [
                    'group_name' => $schedule['group_name'],
                    'start_time' => substr($schedule['start_time'], 0, 5),
                    'end_time'   => substr($schedule['end_time'], 0, 5),
                    'unit_name'  => $schedule['unit_name'],
                    'source'     => 'schedule',
                ],
            ];
        }

        return ['has_session_today' => false, 'session_info' => null];
    }

    private static function extractCardNumber(?string $scannedData): ?string
    {
        if ($scannedData === null || $scannedData === '') {
            return null;
        }

        if (str_starts_with($scannedData, 'THECLUB:SA:')) {
            $parts = explode(':', $scannedData);
            return $parts[2] ?? null;
        }

        if (preg_match('/^SAC-\d{4}-\d{6}$/', $scannedData)) {
            return $scannedData;
        }

        return null;
    }
}
