<?php
declare(strict_types=1);

namespace App\Modules\SportsActivity\Services;

use App\Core\App;

final class MirrorStateService
{
    public static function getFacilityState(int $facilityId, string $date): array
    {
        $db = App::getInstance()->db();

        $facility = $db->selectOne(
            "SELECT * FROM sa_facilities WHERE id = ? AND is_archived = 0",
            [$facilityId]
        );

        if (!$facility) {
            return ['error' => 'المرفق غير موجود'];
        }

        $units = $db->select(
            "SELECT * FROM sa_facility_units WHERE facility_id = ? AND is_active = 1 ORDER BY sort_order",
            [$facilityId]
        );

        $bookings = $db->select(
            "SELECT b.*, fu.name_ar as unit_name, fu.booking_mode, fu.max_capacity,
                    c.full_name_ar as coach_name, g.name_ar as group_name,
                    b.booker_type, b.organization_name, b.passes_total, b.passes_used,
                    b.participant_mode
             FROM sa_bookings b
             JOIN sa_facility_units fu ON fu.id = b.facility_unit_id
             LEFT JOIN sa_coaches c ON c.id = b.coach_id
             LEFT JOIN sa_groups g ON g.id = b.group_id
             WHERE fu.facility_id = ? AND b.booking_date = ?
             AND b.status NOT IN ('cancelled', 'no_show')
             ORDER BY fu.sort_order, b.start_time",
            [$facilityId, $date]
        );

        // Also fetch group schedule entries for today (fallback for ungenerated sessions)
        $dayOfWeek = (int) date('w', strtotime($date));
        $scheduleEntries = $db->select(
            "SELECT gs.facility_unit_id, gs.start_time, gs.end_time, gs.group_id,
                    g.name_ar as group_name, c.full_name_ar as coach_name
             FROM sa_group_schedule gs
             INNER JOIN sa_groups g ON g.id = gs.group_id AND g.status = 'active'
             LEFT JOIN sa_coaches c ON c.id = g.coach_id
             WHERE gs.facility_unit_id IN (SELECT id FROM sa_facility_units WHERE facility_id = ?)
             AND gs.day_of_week = ? AND gs.is_active = 1",
            [$facilityId, $dayOfWeek]
        );

        foreach ($scheduleEntries as $se) {
            $hasMatchingBooking = false;
            foreach ($bookings as $b) {
                if ((int) $b['facility_unit_id'] === (int) $se['facility_unit_id']
                    && $b['start_time'] === $se['start_time']
                    && !empty($b['group_id']) && (int) $b['group_id'] === (int) $se['group_id']) {
                    $hasMatchingBooking = true;
                    break;
                }
            }
            if (!$hasMatchingBooking) {
                $bookings[] = [
                    'id'               => null,
                    'facility_unit_id' => $se['facility_unit_id'],
                    'booking_type'     => 'training',
                    'booking_date'     => $date,
                    'start_time'       => $se['start_time'],
                    'end_time'         => $se['end_time'],
                    'group_id'         => $se['group_id'],
                    'group_name'       => $se['group_name'],
                    'coach_name'       => $se['coach_name'],
                    'status'           => 'planned',
                    'spots_reserved'   => 1,
                    'unit_name'        => null,
                    'booking_mode'     => null,
                    'max_capacity'     => null,
                ];
            }
        }

        if ($facility['facility_type'] === 'pool') {
            $poolZoneBookings = $db->select(
                "SELECT pzb.start_time, pzb.end_time, pzb.status as pzb_status, pzb.assignment_type,
                        pzb.zone_row, pzb.zone_col, g.name_ar as group_name, c.full_name_ar as coach_name
                 FROM sa_pool_zone_bookings pzb
                 LEFT JOIN sa_groups g ON g.id = pzb.group_id
                 LEFT JOIN sa_coaches c ON c.id = g.coach_id
                 WHERE pzb.facility_id = ? AND pzb.booking_date = ? AND pzb.status = 'active'
                 ORDER BY pzb.start_time",
                [$facilityId, $date]
            );

            $unitsBySort = array_values($units);
            foreach ($poolZoneBookings as $pzb) {
                $unitIndex = (int) $pzb['zone_row'];
                if (!isset($unitsBySort[$unitIndex])) {
                    continue;
                }
                $mappedUnitId = (int) $unitsBySort[$unitIndex]['id'];
                $bookings[] = [
                    'id'               => null,
                    'facility_unit_id' => $mappedUnitId,
                    'booking_type'     => $pzb['assignment_type'] ?: 'training',
                    'booking_date'     => $date,
                    'start_time'       => $pzb['start_time'],
                    'end_time'         => $pzb['end_time'],
                    'group_id'         => null,
                    'group_name'       => $pzb['group_name'],
                    'coach_name'       => $pzb['coach_name'],
                    'status'           => 'pool_zone',
                    'spots_reserved'   => 1,
                    'unit_name'        => null,
                    'booking_mode'     => null,
                    'max_capacity'     => null,
                ];
            }
        }

        $hours = json_decode($facility['operating_hours_json'], true) ?: ['start' => '06:00', 'end' => '22:00', 'slot_minutes' => 60];
        $slots = self::buildTimeSlots($hours['start'], $hours['end'], (int) $hours['slot_minutes']);

        $grid = [];
        foreach ($units as $unit) {
            $unitId = (int) $unit['id'];
            $unitBookings = array_filter($bookings, fn($b) => (int) $b['facility_unit_id'] === $unitId);

            $grid[$unitId] = [
                'unit'  => $unit,
                'slots' => [],
            ];

            foreach ($slots as $slot) {
                $slotBookings = array_filter($unitBookings, function ($b) use ($slot) {
                    return $b['start_time'] < $slot['end'] && $b['end_time'] > $slot['start'];
                });

                $occupied = 0;
                foreach ($slotBookings as $sb) {
                    $occupied += (int) $sb['spots_reserved'];
                }

                $cellStatus = 'free';
                if ($unit['booking_mode'] === 'shared') {
                    if ($occupied >= (int) $unit['max_capacity']) {
                        $cellStatus = 'full';
                    } elseif ($occupied > 0) {
                        $cellStatus = 'partial';
                    }
                } else {
                    if (!empty($slotBookings)) {
                        $first = reset($slotBookings);
                        $cellStatus = $first['booking_type'] === 'training' ? 'training' : 'booked';
                        if ($first['booking_type'] === 'maintenance' || $first['booking_type'] === 'blocked') {
                            $cellStatus = 'blocked';
                        }
                    }
                }

                $grid[$unitId]['slots'][] = [
                    'time_start' => $slot['start'],
                    'time_end'   => $slot['end'],
                    'status'     => $cellStatus,
                    'occupied'   => $occupied,
                    'max'        => (int) $unit['max_capacity'],
                    'bookings'   => array_values($slotBookings),
                ];
            }
        }

        return [
            'facility' => $facility,
            'units'    => $units,
            'grid'     => $grid,
            'slots'    => $slots,
            'date'     => $date,
        ];
    }

    public static function getMultiFacilityState(int $disciplineId, string $date): array
    {
        $db = App::getInstance()->db();

        $discipline = $db->selectOne(
            "SELECT * FROM sa_disciplines WHERE id = ? AND is_active = 1 AND is_archived = 0",
            [$disciplineId]
        );

        if (!$discipline) {
            return ['error' => 'اللعبة غير موجودة'];
        }

        $facilities = $db->select(
            "SELECT * FROM sa_facilities WHERE discipline_id = ? AND is_active = 1 AND is_archived = 0 ORDER BY name_ar",
            [$disciplineId]
        );

        if (empty($facilities)) {
            return ['error' => 'لا توجد مرافق مرتبطة بهذه اللعبة'];
        }

        $facilityStates = [];
        foreach ($facilities as $facility) {
            $state = self::getFacilityState((int) $facility['id'], $date);
            if (!isset($state['error'])) {
                $facilityStates[] = $state;
            }
        }

        return [
            'discipline' => $discipline,
            'facilities' => $facilityStates,
            'date'       => $date,
        ];
    }

    private static function buildTimeSlots(string $start, string $end, int $slotMinutes): array
    {
        $slots = [];
        $current = strtotime($start);
        $endTs = strtotime($end);

        while ($current < $endTs) {
            $next = $current + ($slotMinutes * 60);
            $slots[] = [
                'start' => date('H:i', $current),
                'end'   => date('H:i', $next),
            ];
            $current = $next;
        }

        return $slots;
    }
}
