<?php
class Slot
{
    public static function createRecurring(int $professionalId, string $startDate, string $endDate, array $weekdays, array $hours, int $durationMinutes): int
    {
        Database::migrate();
        $pdo = Database::pdo();
        $created = 0;
        $start = new DateTime($startDate);
        $end = new DateTime($endDate);
        $end->setTime(23, 59, 59);

        $findExisting = $pdo->prepare('SELECT id, is_active FROM availability_slots WHERE professional_id = ? AND start_at = ? LIMIT 1');
        $reactivate = $pdo->prepare('UPDATE availability_slots SET end_at = ?, is_active = 1 WHERE id = ?');
        $insert = $pdo->prepare('INSERT INTO availability_slots (professional_id, start_at, end_at, is_active, created_at) VALUES (?, ?, ?, 1, ?)');

        while ($start <= $end) {
            $weekday = (int)$start->format('N'); // 1 segunda, 7 domingo
            if (in_array($weekday, $weekdays, true)) {
                foreach ($hours as $hour) {
                    if (!preg_match('/^\d{2}:\d{2}$/', $hour)) {
                        continue;
                    }
                    $slotStart = DateTime::createFromFormat('Y-m-d H:i', $start->format('Y-m-d') . ' ' . $hour);
                    if (!$slotStart) {
                        continue;
                    }
                    $slotEnd = clone $slotStart;
                    $slotEnd->modify('+' . $durationMinutes . ' minutes');
                    $slotStartText = $slotStart->format('Y-m-d H:i:s');
                    $slotEndText = $slotEnd->format('Y-m-d H:i:s');
                    $findExisting->execute([$professionalId, $slotStartText]);
                    $existing = $findExisting->fetch();
                    if ($existing) {
                        // Mesmo profissional e mesmo horário: não da erro. Reativa/atualiza se estava removido.
                        $reactivate->execute([$slotEndText, (int)$existing['id']]);
                        if ((int)($existing['is_active'] ?? 1) !== 1) {
                            $created++;
                        }
                        continue;
                    }
                    try {
                        $insert->execute([
                            $professionalId,
                            $slotStartText,
                            $slotEndText,
                            date('Y-m-d H:i:s'),
                        ]);
                        $created++;
                    } catch (PDOException $e) {
                        // Slot duplicado: ignora para manter cadastro em lote seguro.
                        // Caso banco antigo ainda tenha UNIQUE(start_at), tenta migrar e segue sem quebrar a tela.
                        if ($e->getCode() === '23000') {
                            Database::migrate();
                            continue;
                        }
                        throw $e;
                    }
                }
            }
            $start->modify('+1 day');
        }

        return $created;
    }

    public static function byMonthForStudent(string $month, int $studentId): array
    {
        $from = DateTime::createFromFormat('Y-m-d H:i:s', $month . '-01 00:00:00') ?: new DateTime('first day of this month 00:00:00');
        $to = clone $from;
        $to->modify('first day of next month');

        $stmt = Database::pdo()->prepare("SELECT s.*, p.name AS professional_name, p.type AS professional_type, p.profile_photo_path AS professional_photo_path,
            a.id AS appointment_id, a.student_id AS booked_student_id
            FROM availability_slots s
            INNER JOIN professionals p ON p.id = s.professional_id
            LEFT JOIN appointments a ON a.active_slot_key = s.id AND a.status = 'booked'
            WHERE s.is_active = 1 AND s.start_at >= ? AND s.start_at < ?
            ORDER BY s.start_at ASC");
        $stmt->execute([$from->format('Y-m-d H:i:s'), $to->format('Y-m-d H:i:s')]);
        return $stmt->fetchAll();
    }


    public static function availableCountForStudent(int $studentId = 0): int
    {
        Database::migrate();
        $stmt = Database::pdo()->prepare("SELECT COUNT(*) FROM availability_slots s
            INNER JOIN professionals p ON p.id=s.professional_id AND p.is_active=1
            LEFT JOIN appointments a ON a.active_slot_key=s.id AND a.status='booked'
            WHERE s.is_active=1 AND s.start_at>? AND a.id IS NULL");
        $stmt->execute([date('Y-m-d H:i:s')]);
        return (int)$stmt->fetchColumn();
    }

    public static function upcoming(int $limit = 10): array
    {
        $stmt = Database::pdo()->prepare("SELECT s.*, p.name AS professional_name,
            CASE WHEN a.id IS NULL THEN 'livre' ELSE 'ocupado' END AS slot_status
            FROM availability_slots s
            INNER JOIN professionals p ON p.id = s.professional_id
            LEFT JOIN appointments a ON a.active_slot_key = s.id AND a.status = 'booked'
            WHERE s.is_active = 1 AND s.start_at >= ?
            ORDER BY s.start_at ASC LIMIT ?");
        $stmt->bindValue(1, date('Y-m-d H:i:s'));
        $stmt->bindValue(2, $limit, PDO::PARAM_INT);
        $stmt->execute();
        return $stmt->fetchAll();
    }

    public static function deactivate(int $slotId): bool
    {
        $stmt = Database::pdo()->prepare("SELECT COUNT(*) FROM appointments WHERE active_slot_key = ? AND status = 'booked'");
        $stmt->execute([$slotId]);
        if ((int)$stmt->fetchColumn() > 0) {
            return false;
        }
        $stmt = Database::pdo()->prepare('UPDATE availability_slots SET is_active = 0 WHERE id = ?');
        $stmt->execute([$slotId]);
        return true;
    }

    public static function unlock(int $slotId): array
    {
        Database::migrate();
        $stmt = Database::pdo()->prepare("SELECT a.id FROM appointments a WHERE a.active_slot_key = ? AND a.status = 'booked' LIMIT 1");
        $stmt->execute([$slotId]);
        $appointmentId = (int)($stmt->fetchColumn() ?: 0);
        if ($appointmentId <= 0) {
            return ['ok' => false, 'message' => 'Este horário não possui aula bloqueada/agendada para desbloquear.'];
        }
        return Appointment::unlockByAdmin($appointmentId);
    }

}
