<?php
declare(strict_types=1);

namespace App\Modules\Sessions\Services;

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

final class SessionManagementService
{
    /**
     * Generate sessions for a given week from training_groups schedules.
     * Returns the count of sessions created.
     */
    public static function generateWeeklySessions(string $weekStartDate): int
    {
        $db = App::getInstance()->db();
        $count = 0;

        // Get all active training groups that have schedule info
        $groups = $db->select(
            "SELECT tg.id, tg.academy_id, tg.coach_id, tg.facility_id,
                    tg.day_of_week, tg.start_time, tg.end_time, tg.current_count
             FROM training_groups tg
             WHERE tg.is_active = 1
               AND tg.is_archived = 0
               AND tg.day_of_week IS NOT NULL
               AND tg.start_time IS NOT NULL
               AND tg.end_time IS NOT NULL"
        );

        $weekStart = new \DateTimeImmutable($weekStartDate);

        foreach ($groups as $group) {
            $dayOfWeek = (int) $group['day_of_week'];

            // Calculate the actual date for this day_of_week in the given week
            // weekStartDate is assumed to be a Sunday (day 0)
            $daysToAdd = $dayOfWeek;
            $sessionDate = $weekStart->modify("+{$daysToAdd} days")->format('Y-m-d');

            // Check if session already exists for this group + date
            $existing = $db->selectOne(
                "SELECT id FROM training_sessions
                 WHERE group_id = ? AND session_date = ? AND status != 'cancelled'",
                [(int) $group['id'], $sessionDate]
            );

            if ($existing) {
                continue;
            }

            // Create the session
            $db->insert('training_sessions', [
                'group_id'         => (int) $group['id'],
                'academy_id'       => (int) $group['academy_id'],
                'coach_id'         => $group['coach_id'] ? (int) $group['coach_id'] : null,
                'facility_id'      => $group['facility_id'] ? (int) $group['facility_id'] : null,
                'session_date'     => $sessionDate,
                'start_time'       => $group['start_time'],
                'end_time'         => $group['end_time'],
                'session_type'     => 'regular',
                'status'           => 'scheduled',
                'players_expected' => (int) $group['current_count'],
            ]);

            $count++;
        }

        return $count;
    }

    /**
     * Cancel a session and issue makeup credits to active group members.
     */
    public static function cancelSession(int $sessionId, string $reason, int $cancelledBy): void
    {
        $db = App::getInstance()->db();

        // Update session status
        $db->update('training_sessions', [
            'status'              => 'cancelled',
            'cancellation_reason' => $reason,
            'cancelled_by'        => $cancelledBy,
        ], 'id = ?', [$sessionId]);

        // Get the session details for group_id
        $session = $db->selectOne(
            "SELECT group_id, session_date FROM training_sessions WHERE id = ?",
            [$sessionId]
        );

        if (!$session) {
            return;
        }

        // Get all active memberships for this group
        $members = $db->select(
            "SELECT gm.player_id, gm.enrollment_id
             FROM group_memberships gm
             WHERE gm.group_id = ? AND gm.status = 'active'",
            [(int) $session['group_id']]
        );

        // Create makeup credits for each member
        $expiresAt = date('Y-m-d', strtotime('+30 days'));
        foreach ($members as $member) {
            $db->insert('makeup_credits', [
                'player_id'         => (int) $member['player_id'],
                'enrollment_id'     => (int) $member['enrollment_id'],
                'missed_session_id' => $sessionId,
                'status'            => 'available',
                'expires_at'        => $expiresAt,
            ]);
        }

        EventBus::dispatch('session.cancelled', [
            'session_id' => $sessionId,
            'reason'     => $reason,
            'group_id'   => (int) $session['group_id'],
        ]);
    }

    /**
     * Record attendance for a session.
     * $attendanceData: array of ['player_id' => int, 'status' => string, 'check_in_time' => ?string, 'enrollment_id' => ?int]
     */
    public static function recordAttendance(int $sessionId, array $attendanceData): void
    {
        $db = App::getInstance()->db();
        $attendedCount = 0;

        $session = $db->selectOne(
            "SELECT group_id FROM training_sessions WHERE id = ?",
            [$sessionId]
        );

        foreach ($attendanceData as $record) {
            $playerId = (int) $record['player_id'];
            $status = $record['status'] ?? 'present';
            $checkInTime = !empty($record['check_in_time']) ? $record['check_in_time'] : null;
            $enrollmentId = !empty($record['enrollment_id']) ? (int) $record['enrollment_id'] : null;

            // Check if attendance record already exists
            $existing = $db->selectOne(
                "SELECT id FROM session_attendance WHERE session_id = ? AND player_id = ?",
                [$sessionId, $playerId]
            );

            $data = [
                'session_id'    => $sessionId,
                'player_id'     => $playerId,
                'enrollment_id' => $enrollmentId,
                'status'        => $status,
                'check_in_time' => $checkInTime,
            ];

            if ($existing) {
                $db->update('session_attendance', $data, 'id = ?', [(int) $existing['id']]);
            } else {
                $db->insert('session_attendance', $data);
            }

            // Count attended (present, late, makeup all count)
            if (in_array($status, ['present', 'late', 'makeup'], true)) {
                $attendedCount++;
            }

            // Create makeup credit for absent players
            if ($status === 'absent' && $enrollmentId && $session) {
                // Check if credit already exists for this session+player
                $existingCredit = $db->selectOne(
                    "SELECT id FROM makeup_credits WHERE missed_session_id = ? AND player_id = ?",
                    [$sessionId, $playerId]
                );

                if (!$existingCredit) {
                    $db->insert('makeup_credits', [
                        'player_id'         => $playerId,
                        'enrollment_id'     => $enrollmentId,
                        'missed_session_id' => $sessionId,
                        'status'            => 'available',
                        'expires_at'        => date('Y-m-d', strtotime('+30 days')),
                    ]);
                }
            }
        }

        // Update session players_attended count
        $db->update('training_sessions', [
            'players_attended' => $attendedCount,
        ], 'id = ?', [$sessionId]);

        EventBus::dispatch('session.attendance.recorded', [
            'session_id'      => $sessionId,
            'players_attended' => $attendedCount,
        ]);
    }

    /**
     * Mark a session as completed.
     */
    public static function completeSession(int $sessionId): void
    {
        $db = App::getInstance()->db();

        $db->update('training_sessions', [
            'status' => 'completed',
        ], 'id = ?', [$sessionId]);

        EventBus::dispatch('session.completed', [
            'session_id' => $sessionId,
        ]);
    }

    /**
     * Use a makeup credit for a session.
     */
    public static function useMakeupCredit(int $creditId, int $sessionId): void
    {
        $db = App::getInstance()->db();

        $db->update('makeup_credits', [
            'status'          => 'used',
            'used_session_id' => $sessionId,
        ], 'id = ?', [$creditId]);
    }
}
