<?php
declare(strict_types=1);

namespace App\Modules\Reservations\Services;

use App\Core\App;
use App\Modules\Reservations\Models\Reservation;
use App\Modules\Facilities\Models\FacilityBlackoutDate;

class FacilityConflictDetector
{
    /**
     * Check for booking conflicts on a facility for a given date and time range.
     *
     * Returns an empty array when no conflicts exist, or an array of conflict
     * descriptions when overlaps are found.
     *
     * @return array<int, array{type: string, message: string}>
     */
    public static function checkConflicts(
        int $facilityId,
        string $date,
        string $startTime,
        string $endTime,
        ?int $excludeReservationId = null,
        ?int $coachId = null
    ): array {
        $conflicts = [];

        // 1. Check overlapping reservations
        $reservationConflicts = self::checkReservationConflicts(
            $facilityId, $date, $startTime, $endTime, $excludeReservationId
        );
        $conflicts = array_merge($conflicts, $reservationConflicts);

        // 2. Check overlapping rental bookings
        $rentalConflicts = self::checkRentalConflicts(
            $facilityId, $date, $startTime, $endTime
        );
        $conflicts = array_merge($conflicts, $rentalConflicts);

        // 3. Check facility blackout dates
        $blackoutConflicts = self::checkBlackoutConflicts(
            $facilityId, $date, $startTime, $endTime
        );
        $conflicts = array_merge($conflicts, $blackoutConflicts);

        // 4. Check training session conflicts on this facility
        $sessionConflicts = self::checkSessionConflicts(
            $facilityId, $date, $startTime, $endTime
        );
        $conflicts = array_merge($conflicts, $sessionConflicts);

        // 5. Check coach scheduling conflicts
        if ($coachId !== null) {
            $coachConflicts = self::checkCoachConflicts(
                $coachId, $date, $startTime, $endTime
            );
            $conflicts = array_merge($conflicts, $coachConflicts);
        }

        return $conflicts;
    }

    /**
     * Check for overlapping reservations on the same facility/date.
     */
    private static function checkReservationConflicts(
        int $facilityId,
        string $date,
        string $startTime,
        string $endTime,
        ?int $excludeReservationId
    ): array {
        $conflicts = [];

        $query = (new \App\Core\QueryBuilder())
            ->table('reservations')
            ->where('facility_id', '=', $facilityId)
            ->where('reservation_date', '=', $date)
            ->whereRaw("`status` NOT IN ('cancelled', 'no_show')")
            ->whereRaw('(`start_time` < ? AND `end_time` > ?)', [$endTime, $startTime]);

        if ($excludeReservationId !== null) {
            $query = $query->whereRaw('`id` != ?', [$excludeReservationId]);
        }

        $rows = $query->get();

        foreach ($rows as $row) {
            $conflicts[] = [
                'type'    => 'reservation',
                'message' => sprintf(
                    'يوجد حجز متعارض (رقم %s) من %s إلى %s',
                    $row['reservation_number'] ?? '—',
                    $row['start_time'] ?? '',
                    $row['end_time'] ?? ''
                ),
            ];
        }

        return $conflicts;
    }

    /**
     * Check for overlapping rental bookings on the same facility/date.
     */
    private static function checkRentalConflicts(
        int $facilityId,
        string $date,
        string $startTime,
        string $endTime
    ): array {
        $conflicts = [];
        $db = App::getInstance()->db();

        // Check if rental_bookings table exists; skip gracefully if not
        try {
            $rows = $db->select(
                "SELECT * FROM `rental_bookings`
                 WHERE `facility_id` = ?
                   AND `booking_date` = ?
                   AND `status` NOT IN ('cancelled')
                   AND (`start_time` < ? AND `end_time` > ?)",
                [$facilityId, $date, $endTime, $startTime]
            );
        } catch (\Throwable) {
            // Table may not exist yet; silently skip
            return [];
        }

        foreach ($rows as $row) {
            $conflicts[] = [
                'type'    => 'rental',
                'message' => sprintf(
                    'يوجد حجز تأجير مؤسسي متعارض من %s إلى %s',
                    $row['start_time'] ?? '',
                    $row['end_time'] ?? ''
                ),
            ];
        }

        return $conflicts;
    }

    /**
     * Check for training session conflicts on this facility.
     */
    private static function checkSessionConflicts(
        int $facilityId,
        string $date,
        string $startTime,
        string $endTime
    ): array {
        $conflicts = [];
        $db = App::getInstance()->db();

        try {
            $rows = $db->select(
                "SELECT ts.*, tg.name_ar AS group_name, c.full_name_ar AS coach_name
                 FROM training_sessions ts
                 LEFT JOIN training_groups tg ON tg.id = ts.group_id
                 LEFT JOIN coaches c ON c.id = ts.coach_id
                 WHERE ts.facility_id = ?
                   AND ts.session_date = ?
                   AND ts.status NOT IN ('cancelled')
                   AND (ts.start_time < ? AND ts.end_time > ?)",
                [$facilityId, $date, $endTime, $startTime]
            );
        } catch (\Throwable) {
            return [];
        }

        foreach ($rows as $row) {
            $conflicts[] = [
                'type'    => 'session',
                'message' => sprintf(
                    'يوجد حصة تدريبية متعارضة (%s) من %s إلى %s — المدرب: %s',
                    $row['group_name'] ?? 'مجموعة',
                    $row['start_time'] ?? '',
                    $row['end_time'] ?? '',
                    $row['coach_name'] ?? '—'
                ),
            ];
        }

        return $conflicts;
    }

    /**
     * Check for coach scheduling conflicts across all facilities.
     */
    private static function checkCoachConflicts(
        int $coachId,
        string $date,
        string $startTime,
        string $endTime
    ): array {
        $conflicts = [];
        $db = App::getInstance()->db();

        try {
            $rows = $db->select(
                "SELECT ts.*, tg.name_ar AS group_name, f.name_ar AS facility_name
                 FROM training_sessions ts
                 LEFT JOIN training_groups tg ON tg.id = ts.group_id
                 LEFT JOIN facilities f ON f.id = ts.facility_id
                 WHERE ts.coach_id = ?
                   AND ts.session_date = ?
                   AND ts.status NOT IN ('cancelled')
                   AND (ts.start_time < ? AND ts.end_time > ?)",
                [$coachId, $date, $endTime, $startTime]
            );
        } catch (\Throwable) {
            return [];
        }

        foreach ($rows as $row) {
            $conflicts[] = [
                'type'    => 'coach',
                'message' => sprintf(
                    'المدرب لديه حصة أخرى في %s (%s) من %s إلى %s',
                    $row['facility_name'] ?? '—',
                    $row['group_name'] ?? '—',
                    $row['start_time'] ?? '',
                    $row['end_time'] ?? ''
                ),
            ];
        }

        return $conflicts;
    }

    /**
     * Suggest alternative time slots when a conflict is detected.
     */
    public static function suggestAlternatives(
        int $facilityId,
        string $date,
        string $startTime,
        string $endTime,
        ?int $coachId = null
    ): array {
        $duration = (strtotime($endTime) - strtotime($startTime)) / 3600;
        $alternatives = [];

        $slots = ['07:00', '08:00', '09:00', '10:00', '11:00', '12:00',
                  '13:00', '14:00', '15:00', '16:00', '17:00', '18:00',
                  '19:00', '20:00', '21:00'];

        foreach ($slots as $slotStart) {
            $slotEnd = date('H:i', strtotime($slotStart) + (int)($duration * 3600));
            if ($slotStart === $startTime) {
                continue;
            }
            $conflicts = self::checkConflicts($facilityId, $date, $slotStart . ':00', $slotEnd . ':00', null, $coachId);
            if (empty($conflicts)) {
                $alternatives[] = [
                    'date'       => $date,
                    'start_time' => $slotStart,
                    'end_time'   => $slotEnd,
                ];
            }
            if (count($alternatives) >= 5) {
                break;
            }
        }

        return $alternatives;
    }

    /**
     * Check for blackout dates on the facility.
     */
    private static function checkBlackoutConflicts(
        int $facilityId,
        string $date,
        string $startTime,
        string $endTime
    ): array {
        $conflicts = [];

        $rows = (new \App\Core\QueryBuilder())
            ->table('facility_blackout_dates')
            ->noSoftDelete()
            ->where('facility_id', '=', $facilityId)
            ->where('blackout_date', '=', $date)
            ->get();

        foreach ($rows as $row) {
            $bStart = $row['start_time'] ?? null;
            $bEnd   = $row['end_time'] ?? null;

            // Full-day blackout
            if (empty($bStart) && empty($bEnd)) {
                $conflicts[] = [
                    'type'    => 'blackout',
                    'message' => 'المرفق محجوب بالكامل في هذا التاريخ: ' . ($row['reason'] ?? ''),
                ];
                continue;
            }

            // Time-range blackout
            $bStart = $bStart ?? '00:00:00';
            $bEnd   = $bEnd ?? '23:59:59';

            if ($startTime < $bEnd && $endTime > $bStart) {
                $conflicts[] = [
                    'type'    => 'blackout',
                    'message' => sprintf(
                        'المرفق محجوب من %s إلى %s: %s',
                        $bStart,
                        $bEnd,
                        $row['reason'] ?? ''
                    ),
                ];
            }
        }

        return $conflicts;
    }
}
