<?php
declare(strict_types=1);

namespace App\Modules\SportsActivity\Services;

use App\Core\App;
use App\Modules\Cashier\Services\PaymentRequestService;
use App\Modules\SportsActivity\SaConstants;

final class EnrollmentService
{
    public static function enroll(int $groupId, int $playerId): array
    {
        $db = App::getInstance()->db();

        $player = $db->selectOne(
            "SELECT * FROM sa_players WHERE id = ? AND is_archived = 0",
            [$playerId]
        );
        if (!$player) {
            return ['success' => false, 'error' => 'اللاعب غير مسجل في النظام'];
        }

        $medicalWarning = null;
        $needsGracePeriod = false;
        if (!in_array($player['medical_status'], [SaConstants::MEDICAL_FIT, SaConstants::MEDICAL_CONDITIONAL])) {
            $medicalWarning = 'اللاعب بدون شهادة طبية سارية — سيتم تعيين مهلة لتقديم الشهادة';
            $needsGracePeriod = true;
        } elseif ($player['medical_status'] === SaConstants::MEDICAL_FIT && !empty($player['medical_expiry_date']) && $player['medical_expiry_date'] < date('Y-m-d')) {
            $medicalWarning = 'الشهادة الطبية منتهية الصلاحية — سيتم تعيين مهلة لتجديدها';
            $needsGracePeriod = true;
        }

        $group = $db->selectOne("SELECT * FROM sa_groups WHERE id = ? AND is_archived = 0", [$groupId]);
        if (!$group) {
            return ['success' => false, 'error' => 'المجموعة غير موجودة'];
        }

        if ($group['status'] !== SaConstants::GROUP_ACTIVE) {
            return ['success' => false, 'error' => 'المجموعة غير نشطة'];
        }

        // Age validation (backwards compatible — skips if columns are NULL)
        if (!empty($group['min_age']) || !empty($group['max_age'])) {
            $playerAge = null;
            if (!empty($player['date_of_birth'])) {
                $playerAge = (int) date_diff(
                    date_create($player['date_of_birth']),
                    date_create()
                )->y;
            }
            if ($playerAge !== null) {
                if (!empty($group['min_age']) && $playerAge < (int) $group['min_age']) {
                    return ['success' => false, 'error' => 'عمر اللاعب أقل من الحد الأدنى للمجموعة (' . $group['min_age'] . ' سنة)'];
                }
                if (!empty($group['max_age']) && $playerAge > (int) $group['max_age']) {
                    return ['success' => false, 'error' => 'عمر اللاعب أكبر من الحد الأقصى للمجموعة (' . $group['max_age'] . ' سنة)'];
                }
            }
        }

        $existing = $db->selectOne(
            "SELECT id FROM sa_group_players WHERE group_id = ? AND player_id = ? AND status IN ('active','pending_payment')",
            [$groupId, $playerId]
        );
        if ($existing) {
            return ['success' => false, 'error' => 'اللاعب مسجل بالفعل في هذه المجموعة'];
        }

        $program = $db->selectOne(
            "SELECT monthly_fee_member, monthly_fee_nonmember, max_capacity FROM sa_programs WHERE id = ?",
            [(int) $group['program_id']]
        );
        if (!$program) {
            return ['success' => false, 'error' => 'البرنامج المرتبط بالمجموعة غير موجود'];
        }

        if ((int) $group['current_count'] >= (int) $program['max_capacity']) {
            return ['success' => false, 'error' => 'المجموعة ممتلئة', 'suggest_waitlist' => true];
        }

        $fee = $player['player_type'] === SaConstants::PLAYER_MEMBER
            ? (string) $program['monthly_fee_member']
            : (string) $program['monthly_fee_nonmember'];

        $employeeId = (int) (App::getInstance()->session()->get('employee_id') ?? 0);

        $graceDeadline = null;
        if ($needsGracePeriod) {
            $graceDaysRow = $db->selectOne(
                "SELECT config_value FROM system_config WHERE config_key = 'sa.medical_grace_days'",
                []
            );
            $graceDays = (int) ($graceDaysRow['config_value'] ?? 14);
            $graceDeadline = date('Y-m-d', strtotime("+{$graceDays} days"));
        }

        $db->beginTransaction();
        try {
            $enrollData = [
                'group_id'    => $groupId,
                'player_id'   => $playerId,
                'enrolled_at' => date('Y-m-d'),
                'status'      => SaConstants::STATUS_PENDING_PAYMENT,
                'created_at'  => date('Y-m-d H:i:s'),
                'updated_at'  => date('Y-m-d H:i:s'),
                'created_by'  => $employeeId,
            ];
            if ($graceDeadline) {
                $enrollData['medical_grace_deadline'] = $graceDeadline;
            }
            $enrollmentId = $db->insert('sa_group_players', $enrollData);

            $memberId = !empty($player['member_id']) ? (int) $player['member_id'] : 0;
            $description = 'تسجيل رياضي — ' . $group['name_ar'] . ' — ' . $player['full_name_ar'];

            $requestResult = PaymentRequestService::createRequest([
                'member_id'           => $memberId,
                'payment_type'        => SaConstants::PAY_TYPE_SPORTS_REG,
                'amount'              => $fee,
                'description_ar'      => $description,
                'related_entity_type' => SaConstants::ENTITY_GROUP_PLAYERS,
                'related_entity_id'   => $enrollmentId,
            ]);

            if ($requestResult['success']) {
                $db->update('sa_group_players', [
                    'payment_request_id' => (int) $requestResult['request_id'],
                    'updated_at'         => date('Y-m-d H:i:s'),
                ], 'id = ?', [$enrollmentId]);
            }

            $db->commit();
        } catch (\Throwable $e) {
            $db->rollBack();
            return ['success' => false, 'error' => 'فشل عملية التسجيل: ' . $e->getMessage()];
        }

        return [
            'success'                => true,
            'enrollment_id'          => $enrollmentId,
            'request_id'             => $requestResult['request_id'] ?? null,
            'request_number'         => $requestResult['request_number'] ?? null,
            'fee'                    => $fee,
            'medical_warning'        => $medicalWarning,
            'medical_grace_deadline' => $graceDeadline,
        ];
    }

    public static function withdraw(int $groupId, int $playerId, string $reason = ''): array
    {
        $db = App::getInstance()->db();

        $db->beginTransaction();
        try {
            $enrollment = $db->selectOne(
                "SELECT id, status FROM sa_group_players WHERE group_id = ? AND player_id = ? AND status IN ('active','pending_payment')",
                [$groupId, $playerId]
            );

            if (!$enrollment) {
                $db->rollBack();
                return ['success' => false, 'error' => 'اللاعب غير مسجل في هذه المجموعة'];
            }

            $wasActive = $enrollment['status'] === SaConstants::STATUS_ACTIVE;

            $db->update('sa_group_players', [
                'status'     => SaConstants::STATUS_WITHDRAWN,
                'left_at'    => date('Y-m-d'),
                'notes'      => $reason ?: null,
                'updated_at' => date('Y-m-d H:i:s'),
            ], 'id = ?', [(int) $enrollment['id']]);

            $newCount = null;
            if ($wasActive) {
                $group = $db->selectOne("SELECT current_count FROM sa_groups WHERE id = ? FOR UPDATE", [$groupId]);
                $newCount = max(0, (int) $group['current_count'] - 1);

                $db->update('sa_groups', [
                    'current_count' => $newCount,
                    'is_full'       => 0,
                    'updated_at'    => date('Y-m-d H:i:s'),
                ], 'id = ?', [$groupId]);
            }

            $db->commit();

            if ($wasActive) {
                WaitlistAutoOfferService::offerNextInLine($groupId);
            }

            return ['success' => true, 'new_count' => $newCount];
        } catch (\Throwable $e) {
            $db->rollBack();
            return ['success' => false, 'error' => 'فشل عملية الانسحاب: ' . $e->getMessage()];
        }
    }

    public static function activateEnrollment(int $enrollmentId, int $paymentId): bool
    {
        $db = App::getInstance()->db();

        $db->beginTransaction();
        try {
            $enrollment = $db->selectOne(
                "SELECT * FROM sa_group_players WHERE id = ? AND status = 'pending_payment'",
                [$enrollmentId]
            );
            if (!$enrollment) {
                $db->rollBack();
                return false;
            }

            $db->update('sa_group_players', [
                'status'                  => SaConstants::STATUS_ACTIVE,
                'activated_by_payment_id' => $paymentId,
                'updated_at'              => date('Y-m-d H:i:s'),
            ], 'id = ?', [$enrollmentId]);

            $group = $db->selectOne(
                "SELECT current_count, max_capacity FROM sa_groups WHERE id = ? FOR UPDATE",
                [(int) $enrollment['group_id']]
            );
            $newCount = (int) $group['current_count'] + 1;
            $isFull = $newCount >= (int) $group['max_capacity'] ? 1 : 0;

            $db->update('sa_groups', [
                'current_count' => $newCount,
                'is_full'       => $isFull,
                'updated_at'    => date('Y-m-d H:i:s'),
            ], 'id = ?', [(int) $enrollment['group_id']]);

            $db->commit();
            return true;
        } catch (\Throwable $e) {
            $db->rollBack();
            return false;
        }
    }

    public static function forceEnroll(int $groupId, int $playerId): array
    {
        $db = App::getInstance()->db();

        $player = $db->selectOne(
            "SELECT * FROM sa_players WHERE id = ? AND is_archived = 0",
            [$playerId]
        );
        if (!$player) {
            return ['success' => false, 'error' => 'اللاعب غير مسجل في النظام'];
        }

        $group = $db->selectOne("SELECT * FROM sa_groups WHERE id = ? AND is_archived = 0", [$groupId]);
        if (!$group) {
            return ['success' => false, 'error' => 'المجموعة غير موجودة'];
        }

        if ($group['status'] !== SaConstants::GROUP_ACTIVE) {
            return ['success' => false, 'error' => 'المجموعة غير نشطة'];
        }

        $existing = $db->selectOne(
            "SELECT id FROM sa_group_players WHERE group_id = ? AND player_id = ? AND status IN ('active','pending_payment')",
            [$groupId, $playerId]
        );
        if ($existing) {
            return ['success' => false, 'error' => 'اللاعب مسجل بالفعل في هذه المجموعة'];
        }

        $program = $db->selectOne(
            "SELECT monthly_fee_member, monthly_fee_nonmember FROM sa_programs WHERE id = ?",
            [(int) $group['program_id']]
        );

        $fee = $player['player_type'] === SaConstants::PLAYER_MEMBER
            ? (string) ($program['monthly_fee_member'] ?? '0')
            : (string) ($program['monthly_fee_nonmember'] ?? '0');

        $employeeId = (int) (App::getInstance()->session()->get('employee_id') ?? 0);

        $db->beginTransaction();
        try {
            $enrollmentId = $db->insert('sa_group_players', [
                'group_id'       => $groupId,
                'player_id'      => $playerId,
                'enrolled_at'    => date('Y-m-d'),
                'status'         => SaConstants::STATUS_PENDING_PAYMENT,
                'force_enrolled' => 1,
                'created_at'     => date('Y-m-d H:i:s'),
                'updated_at'     => date('Y-m-d H:i:s'),
                'created_by'     => $employeeId,
            ]);

            $memberId = !empty($player['member_id']) ? (int) $player['member_id'] : 0;
            $description = 'تسجيل رياضي (تجاوز السعة) — ' . $group['name_ar'] . ' — ' . $player['full_name_ar'];

            $requestResult = PaymentRequestService::createRequest([
                'member_id'           => $memberId,
                'payment_type'        => SaConstants::PAY_TYPE_SPORTS_REG,
                'amount'              => $fee,
                'description_ar'      => $description,
                'related_entity_type' => SaConstants::ENTITY_GROUP_PLAYERS,
                'related_entity_id'   => $enrollmentId,
            ]);

            if ($requestResult['success']) {
                $db->update('sa_group_players', [
                    'payment_request_id' => (int) $requestResult['request_id'],
                    'updated_at'         => date('Y-m-d H:i:s'),
                ], 'id = ?', [$enrollmentId]);
            }

            $newCount = (int) $group['current_count'] + 1;
            $db->update('sa_groups', [
                'current_count' => $newCount,
                'is_full'       => 1,
                'updated_at'    => date('Y-m-d H:i:s'),
            ], 'id = ?', [$groupId]);

            $db->commit();
        } catch (\Throwable $e) {
            $db->rollBack();
            return ['success' => false, 'error' => 'فشل عملية التسجيل: ' . $e->getMessage()];
        }

        return [
            'success'        => true,
            'enrollment_id'  => $enrollmentId,
            'request_id'     => $requestResult['request_id'] ?? null,
            'request_number' => $requestResult['request_number'] ?? null,
            'fee'            => $fee,
            'force_enrolled' => true,
        ];
    }

    public static function findOrCreateAvailableGroup(int $programId): array
    {
        $db = App::getInstance()->db();

        $program = $db->selectOne(
            "SELECT * FROM sa_programs WHERE id = ? AND is_archived = 0",
            [$programId]
        );
        if (!$program) {
            return ['success' => false, 'error' => 'البرنامج غير موجود'];
        }

        $availableGroup = $db->selectOne(
            "SELECT * FROM sa_groups WHERE program_id = ? AND status = 'active' AND is_archived = 0 AND is_full = 0
             ORDER BY id DESC LIMIT 1",
            [$programId]
        );

        if ($availableGroup) {
            return ['success' => true, 'group' => $availableGroup];
        }

        $sourceGroup = $db->selectOne(
            "SELECT * FROM sa_groups WHERE program_id = ? AND is_archived = 0
             ORDER BY id DESC LIMIT 1",
            [$programId]
        );

        if (!$sourceGroup) {
            return ['success' => false, 'error' => 'لا توجد مجموعات مرجعية للبرنامج'];
        }

        $newName = self::incrementGroupName($sourceGroup['name_ar']);
        $employeeId = (int) (App::getInstance()->session()->get('employee_id') ?? 0);

        $db->beginTransaction();
        try {
            $newGroupId = $db->insert('sa_groups', [
                'name_ar'               => $newName,
                'program_id'            => $programId,
                'facility_unit_id'      => $sourceGroup['facility_unit_id'],
                'coach_id'              => $sourceGroup['coach_id'],
                'monthly_fee_member'    => 0,
                'monthly_fee_nonmember' => 0,
                'min_age'               => $sourceGroup['min_age'],
                'max_age'               => $sourceGroup['max_age'],
                'max_capacity'          => (int) ($program['max_capacity'] ?? 20),
                'current_count'         => 0,
                'is_full'               => 0,
                'status'                => 'active',
                'branch_id'             => $sourceGroup['branch_id'] ?? null,
                'created_by'            => $employeeId,
                'created_at'            => date('Y-m-d H:i:s'),
                'updated_at'            => date('Y-m-d H:i:s'),
            ]);

            $schedules = $db->select(
                "SELECT * FROM sa_group_schedule WHERE group_id = ? AND is_active = 1",
                [(int) $sourceGroup['id']]
            );

            foreach ($schedules as $sch) {
                $db->insert('sa_group_schedule', [
                    'group_id'         => $newGroupId,
                    'facility_unit_id' => $sch['facility_unit_id'],
                    'day_of_week'      => $sch['day_of_week'],
                    'start_time'       => $sch['start_time'],
                    'end_time'         => $sch['end_time'],
                    'is_active'        => 1,
                    'created_at'       => date('Y-m-d H:i:s'),
                    'updated_at'       => date('Y-m-d H:i:s'),
                ]);
            }

            $db->commit();

            $newGroup = $db->selectOne("SELECT * FROM sa_groups WHERE id = ?", [$newGroupId]);
            return ['success' => true, 'group' => $newGroup, 'auto_created' => true];
        } catch (\Throwable $e) {
            $db->rollBack();
            return ['success' => false, 'error' => 'فشل إنشاء مجموعة جديدة: ' . $e->getMessage()];
        }
    }

    private static function incrementGroupName(string $name): string
    {
        if (preg_match('/^(.+)\s(\d+)$/', $name, $matches)) {
            return $matches[1] . ' ' . ((int) $matches[2] + 1);
        }
        return $name . ' 2';
    }

    public static function enrollByCoach(int $programId, int $coachId, int $playerId): array
    {
        $db = App::getInstance()->db();

        $program = $db->selectOne(
            "SELECT * FROM sa_programs WHERE id = ? AND is_archived = 0",
            [$programId]
        );
        if (!$program) {
            return ['success' => false, 'error' => 'البرنامج غير موجود'];
        }

        // Find a group under this program with this coach that has capacity
        $group = $db->selectOne("
            SELECT g.* FROM sa_groups g
            WHERE g.program_id = ? AND g.status = 'active' AND g.is_archived = 0 AND g.is_full = 0
            AND (
                g.coach_id = ?
                OR EXISTS (SELECT 1 FROM sa_group_coaches gc WHERE gc.group_id = g.id AND gc.coach_id = ?)
            )
            ORDER BY g.current_count ASC
            LIMIT 1
        ", [$programId, $coachId, $coachId]);

        if ($group) {
            return self::enroll((int) $group['id'], $playerId);
        }

        // No available group — auto-create one
        $coach = $db->selectOne("SELECT full_name_ar FROM sa_coaches WHERE id = ?", [$coachId]);
        $coachName = $coach['full_name_ar'] ?? 'مدرب';
        $newName = $program['name_ar'] . ' - ' . $coachName;
        $newCode = 'GRP-' . strtoupper(substr(uniqid(), -6));

        $sourceGroup = $db->selectOne(
            "SELECT * FROM sa_groups WHERE program_id = ? AND is_archived = 0 ORDER BY id DESC LIMIT 1",
            [$programId]
        );

        $employeeId = (int) (App::getInstance()->session()->get('employee_id') ?? 0);

        $db->beginTransaction();
        try {
            $newGroupId = $db->insert('sa_groups', [
                'code'                  => $newCode,
                'name_ar'               => $newName,
                'program_id'            => $programId,
                'coach_id'              => $coachId,
                'monthly_fee_member'    => 0,
                'monthly_fee_nonmember' => 0,
                'min_age'               => $sourceGroup['min_age'] ?? null,
                'max_age'               => $sourceGroup['max_age'] ?? null,
                'max_capacity'          => (int) ($program['max_capacity'] ?? 20),
                'current_count'         => 0,
                'is_full'               => 0,
                'status'                => 'active',
                'created_by'            => $employeeId,
                'created_at'            => date('Y-m-d H:i:s'),
                'updated_at'            => date('Y-m-d H:i:s'),
            ]);

            // Add coach to pivot table
            $db->insert('sa_group_coaches', [
                'group_id'   => $newGroupId,
                'coach_id'   => $coachId,
                'role'        => 'primary',
                'created_at' => date('Y-m-d H:i:s'),
            ]);

            // Copy schedule from source if available
            if ($sourceGroup) {
                $schedules = $db->select(
                    "SELECT * FROM sa_group_schedule WHERE group_id = ? AND is_active = 1",
                    [(int) $sourceGroup['id']]
                );
                foreach ($schedules as $sch) {
                    $db->insert('sa_group_schedule', [
                        'group_id'         => $newGroupId,
                        'facility_unit_id' => $sch['facility_unit_id'],
                        'day_of_week'      => $sch['day_of_week'],
                        'start_time'       => $sch['start_time'],
                        'end_time'         => $sch['end_time'],
                        'is_active'        => 1,
                        'created_at'       => date('Y-m-d H:i:s'),
                        'updated_at'       => date('Y-m-d H:i:s'),
                    ]);
                }
            }

            $db->commit();
        } catch (\Throwable $e) {
            $db->rollBack();
            return ['success' => false, 'error' => 'فشل إنشاء المجموعة: ' . $e->getMessage()];
        }

        // Now enroll the player in the newly created group
        return self::enroll($newGroupId, $playerId);
    }

    public static function deactivateEnrollment(int $enrollmentId): bool
    {
        $db = App::getInstance()->db();

        $db->beginTransaction();
        try {
            $enrollment = $db->selectOne(
                "SELECT * FROM sa_group_players WHERE id = ? AND status = 'active'",
                [$enrollmentId]
            );
            if (!$enrollment) {
                $db->rollBack();
                return false;
            }

            $db->update('sa_group_players', [
                'status'                  => SaConstants::STATUS_PENDING_PAYMENT,
                'activated_by_payment_id' => null,
                'updated_at'              => date('Y-m-d H:i:s'),
            ], 'id = ?', [$enrollmentId]);

            $group = $db->selectOne(
                "SELECT current_count FROM sa_groups WHERE id = ? FOR UPDATE",
                [(int) $enrollment['group_id']]
            );
            $newCount = max(0, (int) $group['current_count'] - 1);

            $db->update('sa_groups', [
                'current_count' => $newCount,
                'is_full'       => 0,
                'updated_at'    => date('Y-m-d H:i:s'),
            ], 'id = ?', [(int) $enrollment['group_id']]);

            $db->commit();
            return true;
        } catch (\Throwable $e) {
            $db->rollBack();
            return false;
        }
    }
}
