<?php
declare(strict_types=1);

function service_duration(int $durationId): ?array
{
    $stmt = db()->prepare('SELECT sd.*,s.name service_name,s.slug service_slug,s.buffer_minutes,s.required_therapists,s.is_active FROM service_durations sd JOIN services s ON s.id=sd.service_id WHERE sd.id=? AND sd.is_active=1 AND s.is_active=1 LIMIT 1');
    $stmt->execute([$durationId]);
    return $stmt->fetch() ?: null;
}

function available_slots(int $durationId, string $date, string $genderPreference = 'no_preference', string $sessionType = 'single'): array
{
    $duration = service_duration($durationId);
    if (!$duration) return [];
    if (!valid_iso_date($date) || $date < date('Y-m-d')) return [];
    $ts = strtotime($date . ' 12:00:00');
    $weekday = (int) date('N', $ts);
    $stmt = db()->prepare('SELECT * FROM business_hours WHERE weekday=? LIMIT 1');
    $stmt->execute([$weekday]);
    $hours = $stmt->fetch();
    if (!$hours || (int)$hours['is_closed']) return [];
    $serviceId = (int)$duration['service_id'];
    $minutes = (int)$duration['duration_minutes'];
    $buffer = (int)$duration['buffer_minutes'];
    $step = max(15, (int) setting('slot_step_minutes', 30));
    $open = strtotime($date . ' ' . $hours['open_time']);
    $close = strtotime($date . ' ' . $hours['close_time']);
    $slots = [];
    for ($start=$open; $start + ($minutes+$buffer)*60 <= $close; $start += $step*60) {
        if ($date === date('Y-m-d') && $start < time() + max(0,(int)setting('reservation_notice_hours',2))*3600) continue;
        $end = $start + ($minutes+$buffer)*60;
        $startTime = date('H:i:s', $start);
        $endTime = date('H:i:s', $end);
        if (find_booking_resources($serviceId, $date, $startTime, $endTime, null, $genderPreference, $sessionType, (int)$duration['required_therapists'])) {
            $slots[] = ['value'=>date('H:i', $start),'label'=>date('H:i', $start)];
        }
    }
    return $slots;
}

function find_booking_resources(
    int $serviceId,
    string $date,
    string $start,
    string $end,
    ?int $therapistId = null,
    string $genderPreference = 'no_preference',
    string $sessionType = 'single',
    int $serviceRequiredTherapists = 1
): ?array {
    $weekday = (int) date('N', strtotime($date));
    $params = [$weekday, $start, $end, $serviceId];
    $sql = "SELECT t.id,t.name,t.gender
            FROM therapists t
            JOIN therapist_services ts ON ts.therapist_id=t.id
            JOIN therapist_schedules sch ON sch.therapist_id=t.id AND sch.weekday=? AND sch.is_off=0 AND sch.start_time<=? AND sch.end_time>=?
            WHERE ts.service_id=? AND t.is_active=1";
    if ($therapistId) { $sql .= ' AND t.id=?'; $params[] = $therapistId; }
    if (in_array($genderPreference, ['female','male'], true)) { $sql .= ' AND t.gender=?'; $params[] = $genderPreference; }
    $sql .= " AND NOT EXISTS (
                SELECT 1 FROM reservations r
                WHERE (r.therapist_id=t.id OR r.second_therapist_id=t.id)
                AND r.reservation_date=? AND r.status NOT IN ('cancelled','rejected')
                AND r.start_time < ? AND r.end_time > ?
              )
              AND NOT EXISTS (
                SELECT 1 FROM blocked_times b
                WHERE b.block_date=?
                AND (b.therapist_id=t.id OR (b.therapist_id IS NULL AND b.room_id IS NULL))
                AND (b.is_all_day=1 OR (b.start_time < ? AND b.end_time > ?))
              )
              ORDER BY t.sort_order,t.id";
    array_push($params, $date, $end, $start, $date, $end, $start);
    $stmt = db()->prepare($sql);
    $stmt->execute($params);
    $therapists = $stmt->fetchAll();
    $requiredTherapists = max($sessionType === 'couple' ? 2 : 1, max(1,$serviceRequiredTherapists));
    if (count($therapists) < $requiredTherapists) return null;

    $partySize = $sessionType === 'couple' ? 2 : 1;
    $minCapacity = $partySize;
    $stmt = db()->prepare("SELECT r.id,r.name,r.capacity
        FROM rooms r JOIN room_services rs ON rs.room_id=r.id
        WHERE rs.service_id=? AND r.is_active=1 AND r.capacity>=?
        AND (
          (r.room_type='wet_area' AND (
            SELECT COALESCE(SUM(CASE WHEN b.session_type='couple' THEN 2 ELSE 1 END),0)
            FROM reservations b
            WHERE b.room_id=r.id AND b.reservation_date=? AND b.status NOT IN ('cancelled','rejected')
            AND b.start_time < ? AND b.end_time > ?
          ) + ? <= r.capacity)
          OR
          (r.room_type<>'wet_area' AND NOT EXISTS (
            SELECT 1 FROM reservations b
            WHERE b.room_id=r.id AND b.reservation_date=? AND b.status NOT IN ('cancelled','rejected')
            AND b.start_time < ? AND b.end_time > ?
          ))
        )
        AND NOT EXISTS (
          SELECT 1 FROM blocked_times x
          WHERE x.block_date=?
          AND (x.room_id=r.id OR (x.room_id IS NULL AND x.therapist_id IS NULL))
          AND (x.is_all_day=1 OR (x.start_time < ? AND x.end_time > ?))
        )
        ORDER BY r.sort_order,r.id");
    $stmt->execute([$serviceId,$minCapacity,$date,$end,$start,$partySize,$date,$end,$start,$date,$end,$start]);
    $rooms = $stmt->fetchAll();
    if (!$rooms) return null;
    return [
        'therapist'=>$therapists[0],
        'second_therapist'=>$requiredTherapists === 2 ? $therapists[1] : null,
        'room'=>$rooms[0],
    ];
}

function create_reservation(array $data): array
{
    $duration = service_duration((int)$data['duration_id']);
    if (!$duration) throw new RuntimeException('Seçilen hizmet veya süre artık kullanılamıyor.');
    $date = (string)($data['reservation_date'] ?? '');
    $rawStart = (string)($data['start_time'] ?? '');
    if (!valid_iso_date($date) || !preg_match('/^\d{2}:\d{2}$/', $rawStart)) {
        throw new RuntimeException('Tarih veya saat biçimi geçersiz.');
    }
    $start = $rawStart . ':00';
    $genderPreference = in_array(($data['therapist_preference'] ?? ''), ['female','male','no_preference'], true) ? $data['therapist_preference'] : 'no_preference';
    $sessionType = ($data['session_type'] ?? '') === 'couple' ? 'couple' : 'single';
    $validSlots = array_column(available_slots((int)$duration['id'], $date, $genderPreference, $sessionType), 'value');
    if (!in_array($rawStart, $validSlots, true)) {
        throw new RuntimeException('Seçilen tarih ve saat artık uygun değil. Lütfen uygun saatleri yeniden kontrol edin.');
    }
    $end = date('H:i:s', strtotime($date.' '.$start) + ((int)$duration['duration_minutes'] + (int)$duration['buffer_minutes']) * 60);
    $lockName = 'nekrut-booking-' . $date;
    $pdo = db();
    $lockAcquired = false;
    try {
        $lock = $pdo->prepare('SELECT GET_LOCK(?,5)');
        $lock->execute([$lockName]);
        if ((int)$lock->fetchColumn() !== 1) throw new RuntimeException('Rezervasyon yoğunluğu nedeniyle işlem tamamlanamadı. Tekrar deneyin.');
        $lockAcquired = true;
        $pdo->beginTransaction();
        $requestedTherapist = !empty($data['therapist_id']) ? (int)$data['therapist_id'] : null;
        $resources = find_booking_resources((int)$duration['service_id'], $date, $start, $end, $requestedTherapist, $genderPreference, $sessionType, (int)$duration['required_therapists']);
        if (!$resources) throw new RuntimeException('Bu saat seçtiğiniz tercihler için az önce doldu. Lütfen başka bir saat seçin.');
        $code = 'NKR-' . date('ymd') . '-' . strtoupper(bin2hex(random_bytes(8)));
        $token = bin2hex(random_bytes(32));
        $stmt = $pdo->prepare("INSERT INTO reservations
            (reservation_code,manage_token,service_id,duration_id,therapist_id,second_therapist_id,room_id,reservation_date,start_time,end_time,guest_type,session_type,therapist_preference,guest_name,guest_email,guest_phone,notes,kvkk_consent,terms_consent,status,created_at,updated_at)
            VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,'pending',NOW(),NOW())");
        $stmt->execute([
            $code,$token,$duration['service_id'],$duration['id'],$resources['therapist']['id'],$resources['second_therapist']['id'] ?? null,$resources['room']['id'],$date,$start,$end,
            ($data['guest_type'] ?? '') === 'hotel_guest' ? 'hotel_guest' : 'external',$sessionType,$genderPreference,
            trim((string)$data['guest_name']),mb_strtolower(trim((string)$data['guest_email'])),trim((string)$data['guest_phone']),trim((string)($data['notes'] ?? '')),
            !empty($data['kvkk_consent']) ? 1 : 0,!empty($data['terms_consent']) ? 1 : 0
        ]);
        $id = (int)$pdo->lastInsertId();
        $pdo->commit();
        $pdo->prepare('SELECT RELEASE_LOCK(?)')->execute([$lockName]);
        $lockAcquired = false;
        audit_log('create', 'reservations', $id, $code);
        return ['id'=>$id,'code'=>$code,'token'=>$token];
    } catch (Throwable $e) {
        if ($pdo->inTransaction()) $pdo->rollBack();
        if ($lockAcquired) { try { $pdo->prepare('SELECT RELEASE_LOCK(?)')->execute([$lockName]); } catch (Throwable) {} }
        throw $e;
    }
}
