<?php
declare(strict_types=1);

namespace App\Modules\SportsActivity\Services;

use App\Core\App;
use App\Modules\Payments\Services\PaymentService;
use App\Modules\SportsActivity\SaConstants;

final class BookingService
{
    public static function createHourlyBooking(array $data): array
    {
        $db = App::getInstance()->db();

        $unitId = (int) ($data['facility_unit_id'] ?? 0);
        $date = $data['booking_date'] ?? '';
        $startTime = $data['start_time'] ?? '';
        $endTime = $data['end_time'] ?? '';
        $bookerType = $data['booker_type'] ?? SaConstants::BOOKER_GUEST;
        $bookerId = isset($data['booker_id']) ? (int) $data['booker_id'] : null;
        $bookerName = $data['booker_name'] ?? '';
        $organizationName = $data['organization_name'] ?? null;
        $organizationContact = $data['organization_contact'] ?? null;
        $participantMode = $data['participant_mode'] ?? SaConstants::PARTICIPANT_MODE_PASSES;
        $isMember = ($bookerType === SaConstants::BOOKER_MEMBER);

        $unit = $db->selectOne("SELECT expected_capacity FROM sa_facility_units WHERE id = ?", [$unitId]);
        $expectedCapacity = $unit ? (int) $unit['expected_capacity'] : 1;
        $participantCount = (int) ($data['participant_count'] ?? $expectedCapacity);
        $spotsReserved = (int) ($data['spots_reserved'] ?? $participantCount);

        $availability = SlotAvailabilityService::check($unitId, $date, $startTime, $endTime, $spotsReserved);
        if (!$availability['available']) {
            return ['success' => false, 'error' => $availability['reason']];
        }

        $pricing = PricingCalculatorService::calculate($unitId, $date, $startTime, $endTime, $participantCount, $bookerType);
        $totalAmount = $pricing ? $pricing['total_amount'] : 0.00;

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

        $db->beginTransaction();
        try {
            $bookingData = [
                'booking_number'       => $bookingNumber,
                'facility_unit_id'     => $unitId,
                'booking_type'         => SaConstants::BOOKING_TYPE_HOURLY,
                'booking_date'         => $date,
                'start_time'           => $startTime,
                'end_time'             => $endTime,
                'booker_type'          => $bookerType,
                'booker_id'            => $bookerId,
                'booker_name'          => $bookerName,
                'organization_name'    => $organizationName,
                'organization_contact' => $organizationContact,
                'participant_count'    => $participantCount,
                'participant_mode'     => $participantMode,
                'passes_total'         => ($participantMode === SaConstants::PARTICIPANT_MODE_PASSES) ? $participantCount : 0,
                'passes_used'          => 0,
                'spots_reserved'       => $spotsReserved,
                'total_amount'         => $totalAmount,
                'payment_status'       => SaConstants::PAYMENT_UNPAID,
                'status'               => SaConstants::BOOKING_CONFIRMED,
                'notes'                => $data['notes'] ?? null,
                'branch_id'            => $data['branch_id'] ?? null,
                'created_by'           => $employeeId,
                'created_at'           => date('Y-m-d H:i:s'),
                'updated_at'           => date('Y-m-d H:i:s'),
            ];

            $db->insert('sa_bookings', $bookingData);
            $bookingId = (int) $db->lastInsertId();

            if (!empty($data['participants']) && is_array($data['participants'])) {
                foreach ($data['participants'] as $p) {
                    $db->insert('sa_booking_participants', [
                        'booking_id'       => $bookingId,
                        'player_id'        => isset($p['player_id']) ? (int) $p['player_id'] : null,
                        'participant_name' => $p['name'] ?? null,
                        'is_member'        => (int) ($p['is_member'] ?? 0),
                        'price_charged'    => $pricing ? $pricing['price_per_person'] * $pricing['duration_hours'] : 0,
                        'created_at'       => date('Y-m-d H:i:s'),
                    ]);
                }
            }

            if ($participantMode === SaConstants::PARTICIPANT_MODE_PASSES && $participantCount > 0) {
                BookingPassService::generatePasses($bookingId, $participantCount);
            }

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

        return [
            'success'        => true,
            'booking_id'     => $bookingId,
            'booking_number' => $bookingNumber,
            'total_amount'   => $totalAmount,
            'pricing'        => $pricing,
        ];
    }

    public static function createTrainingBooking(
        int $groupId,
        int $facilityUnitId,
        string $date,
        string $startTime,
        string $endTime,
        ?int $coachId = null
    ): array {
        $db = App::getInstance()->db();

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

        $unit = $db->selectOne("SELECT * FROM sa_facility_units WHERE id = ?", [$facilityUnitId]);
        if (!$unit) {
            return ['success' => false, 'error' => 'الوحدة غير موجودة'];
        }

        $spotsNeeded = $unit['booking_mode'] === 'shared' ? (int) $group['current_count'] : 1;

        $availability = SlotAvailabilityService::check($facilityUnitId, $date, $startTime, $endTime, $spotsNeeded);
        if (!$availability['available']) {
            return ['success' => false, 'error' => $availability['reason']];
        }

        if ($coachId) {
            $conflicts = ConflictDetectionService::check($facilityUnitId, $date, $startTime, $endTime, $coachId);
            $blocking = array_filter($conflicts, fn($c) => $c['severity'] === 'blocking');
            if (!empty($blocking)) {
                return ['success' => false, 'error' => $blocking[0]['message']];
            }
        }

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

        $db->beginTransaction();
        try {
            $db->insert('sa_bookings', [
                'booking_number'   => $bookingNumber,
                'facility_unit_id' => $facilityUnitId,
                'booking_type'     => SaConstants::BOOKING_TYPE_TRAINING,
                'booking_date'     => $date,
                'start_time'       => $startTime,
                'end_time'         => $endTime,
                'group_id'         => $groupId,
                'coach_id'         => $coachId ?? (int) $group['coach_id'],
                'participant_count' => (int) $group['current_count'],
                'spots_reserved'   => $spotsNeeded,
                'total_amount'     => 0.00,
                'payment_status'   => SaConstants::PAYMENT_UNPAID,
                'status'           => SaConstants::BOOKING_CONFIRMED,
                'is_recurring'     => 1,
                'branch_id'        => null,
                'created_by'       => $employeeId,
                'created_at'       => date('Y-m-d H:i:s'),
                'updated_at'       => date('Y-m-d H:i:s'),
            ]);

            $bookingId = (int) $db->lastInsertId();
            $db->commit();
        } catch (\Throwable $e) {
            $db->rollBack();
            return ['success' => false, 'error' => 'فشل إنشاء حجز التدريب: ' . $e->getMessage()];
        }

        return [
            'success'        => true,
            'booking_id'     => $bookingId,
            'booking_number' => $bookingNumber,
        ];
    }

    public static function postpone(int $bookingId, string $newDate, string $newStartTime, string $newEndTime, string $reason = ''): array
    {
        $db = App::getInstance()->db();
        $booking = $db->selectOne("SELECT * FROM sa_bookings WHERE id = ?", [$bookingId]);

        if (!$booking) {
            return ['success' => false, 'error' => 'الحجز غير موجود'];
        }

        if (in_array($booking['status'], [SaConstants::BOOKING_CANCELLED, SaConstants::BOOKING_NO_SHOW])) {
            return ['success' => false, 'error' => 'لا يمكن تأجيل حجز ملغي'];
        }

        $maxPostpone = 3;
        if ((int) ($booking['postponed_count'] ?? 0) >= $maxPostpone) {
            return ['success' => false, 'error' => "تم الوصول للحد الأقصى للتأجيل ({$maxPostpone} مرات)"];
        }

        $unitId = (int) $booking['facility_unit_id'];
        $spotsReserved = (int) $booking['spots_reserved'];

        $availability = SlotAvailabilityService::check($unitId, $newDate, $newStartTime, $newEndTime, $spotsReserved, $bookingId);
        if (!$availability['available']) {
            return ['success' => false, 'error' => 'الموعد الجديد غير متاح: ' . $availability['reason']];
        }

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

        $db->beginTransaction();
        try {
            $db->insert('sa_booking_changes', [
                'booking_id'     => $bookingId,
                'change_type'    => 'postponed',
                'old_date'       => $booking['booking_date'],
                'new_date'       => $newDate,
                'old_start_time' => $booking['start_time'],
                'new_start_time' => $newStartTime,
                'old_end_time'   => $booking['end_time'],
                'new_end_time'   => $newEndTime,
                'reason'         => $reason ?: null,
                'changed_by'     => $employeeId,
                'created_at'     => date('Y-m-d H:i:s'),
            ]);

            $updateData = [
                'booking_date'    => $newDate,
                'start_time'      => $newStartTime,
                'end_time'        => $newEndTime,
                'postponed_count' => (int) ($booking['postponed_count'] ?? 0) + 1,
                'updated_at'      => date('Y-m-d H:i:s'),
                'updated_by'      => $employeeId,
            ];

            if (empty($booking['original_date'])) {
                $updateData['original_date'] = $booking['booking_date'];
                $updateData['original_start_time'] = $booking['start_time'];
            }

            $db->update('sa_bookings', $updateData, 'id = ?', [$bookingId]);

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

        return ['success' => true, 'postponed_count' => (int) ($booking['postponed_count'] ?? 0) + 1];
    }

    public static function cancel(int $bookingId, string $reason = ''): array
    {
        $db = App::getInstance()->db();
        $booking = $db->selectOne("SELECT * FROM sa_bookings WHERE id = ?", [$bookingId]);

        if (!$booking) {
            return ['success' => false, 'error' => 'الحجز غير موجود'];
        }

        if (in_array($booking['status'], [SaConstants::BOOKING_CANCELLED, SaConstants::BOOKING_NO_SHOW])) {
            return ['success' => false, 'error' => 'الحجز ملغي بالفعل'];
        }

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

        $db->update('sa_bookings', [
            'status'              => SaConstants::BOOKING_CANCELLED,
            'cancellation_reason' => $reason,
            'cancelled_by'        => $employeeId,
            'cancelled_at'        => date('Y-m-d H:i:s'),
            'updated_at'          => date('Y-m-d H:i:s'),
        ], 'id = ?', [$bookingId]);

        // Auto-void payment if paid and within 24-hour void window
        if ($booking['payment_status'] === SaConstants::PAYMENT_PAID && !empty($booking['payment_id'])) {
            $voidResult = PaymentService::voidPayment((int) $booking['payment_id'], 'إلغاء حجز: ' . ($reason ?: 'بدون سبب'));
            if ($voidResult['success'] ?? false) {
                $db->update('sa_bookings', [
                    'payment_status' => SaConstants::PAYMENT_REFUNDED,
                    'updated_at'     => date('Y-m-d H:i:s'),
                ], 'id = ?', [$bookingId]);
            }
        }

        return ['success' => true];
    }
}
