<?php

declare(strict_types=1);

require DENTAL_ROOT . '/app/bootstrap.php';

function calendar_escape(string $value): string
{
    return str_replace(["\\", ";", ",", "\r\n", "\r", "\n"], ["\\\\", "\\;", "\\,", "\\n", "\\n", "\\n"], $value);
}

function calendar_utc(string $value, string $timezone): string
{
    $zone = new DateTimeZone($timezone ?: 'Asia/Manila');
    $date = new DateTime($value, $zone);
    $date->setTimezone(new DateTimeZone('UTC'));
    return $date->format('Ymd\THis\Z');
}

function calendar_event_lines(array $event): array
{
    $lines = ['BEGIN:VEVENT'];
    $lines[] = 'UID:' . calendar_escape((string)$event['uid']);
    $lines[] = 'DTSTAMP:' . gmdate('Ymd\THis\Z');
    if (!empty($event['all_day'])) {
        $lines[] = 'DTSTART;VALUE=DATE:' . date('Ymd', strtotime((string)$event['start']));
        $lines[] = 'DTEND;VALUE=DATE:' . date('Ymd', strtotime((string)$event['end']));
    } else {
        $timezone = (string)($event['timezone'] ?? 'Asia/Manila');
        $lines[] = 'DTSTART:' . calendar_utc((string)$event['start'], $timezone);
        $lines[] = 'DTEND:' . calendar_utc((string)$event['end'], $timezone);
    }
    $lines[] = 'SUMMARY:' . calendar_escape((string)$event['summary']);
    if (!empty($event['description'])) $lines[] = 'DESCRIPTION:' . calendar_escape((string)$event['description']);
    if (!empty($event['location'])) $lines[] = 'LOCATION:' . calendar_escape((string)$event['location']);
    if (!empty($event['status'])) $lines[] = 'STATUS:' . calendar_escape((string)$event['status']);
    $lines[] = 'END:VEVENT';
    return $lines;
}

function calendar_output(array $events, string $fileName, string $calendarName): never
{
    $lines = [
        'BEGIN:VCALENDAR',
        'VERSION:2.0',
        'PRODID:-//DentalCare//Clinic Calendar//EN',
        'CALSCALE:GREGORIAN',
        'METHOD:PUBLISH',
        'X-WR-CALNAME:' . calendar_escape($calendarName),
    ];
    foreach ($events as $event) $lines = array_merge($lines, calendar_event_lines($event));
    $lines[] = 'END:VCALENDAR';
    header('Content-Type: text/calendar; charset=utf-8');
    header('Content-Disposition: attachment; filename="' . preg_replace('/[^A-Za-z0-9._-]+/', '_', $fileName) . '"');
    header('Cache-Control: private, no-store, max-age=0');
    echo implode("\r\n", $lines) . "\r\n";
    exit;
}

$pdo = db();
$type = (string)($_GET['type'] ?? 'appointment');

if ($type === 'appointment') {
    $appointmentId = (int)($_GET['appointment_id'] ?? 0);
    $stmt = $pdo->prepare('SELECT a.*,p.first_name,p.last_name,p.email patient_email,b.name branch_name,b.address branch_address,b.timezone branch_timezone FROM `' . table('appointments') . '` a JOIN `' . table('patients') . '` p ON p.id=a.patient_id LEFT JOIN `' . table('branches') . '` b ON b.id=a.branch_id WHERE a.id=? LIMIT 1');
    $stmt->execute([$appointmentId]);
    $appointment = $stmt->fetch();
    if (!$appointment) { http_response_code(404); exit('Appointment not found.'); }

    $authorized = false;
    $token = trim((string)($_GET['token'] ?? ''));
    if ($token !== '' && hash_equals(appointment_calendar_token($appointment), $token)) $authorized = true;
    $portal = patient_portal_user();
    if ($portal && (int)$portal['patient_id'] === (int)$appointment['patient_id']) $authorized = true;
    if (current_user()) {
        require_login();
        require_patient((int)$appointment['patient_id']);
        if (active_role_is('dentist') && (int)$appointment['dentist_user_id'] !== (int)(current_user()['id'] ?? 0)) {
            access_denied('This appointment belongs to another dentist.');
        }
        $authorized = true;
    }
    if (!$authorized) access_denied('A valid appointment calendar link or signed-in account is required.');

    $timezone = (string)($appointment['branch_timezone'] ?: app_config('app.timezone', 'Asia/Manila'));
    $description = 'Dentist: ' . (string)$appointment['dentist_name'] . "\nService: " . (string)$appointment['service_name'];
    if (!empty($appointment['chair_name'])) $description .= "\nChair: " . (string)$appointment['chair_name'];
    if (!empty($appointment['reason_for_visit'])) $description .= "\nReason: " . (string)$appointment['reason_for_visit'];
    $events = [[
        'uid'=>'appointment-' . (int)$appointment['id'] . '@' . dental_current_host(),
        'start'=>(string)$appointment['start_at'],
        'end'=>(string)$appointment['end_at'],
        'timezone'=>$timezone,
        'summary'=>'Dental appointment — ' . (string)$appointment['service_name'],
        'description'=>$description,
        'location'=>trim((string)($appointment['branch_name'] ?? '') . (($appointment['branch_address'] ?? '') ? ', ' . (string)$appointment['branch_address'] : '')),
        'status'=>in_array((string)$appointment['status'], ['cancelled','no_show'], true) ? 'CANCELLED' : 'CONFIRMED',
    ]];
    calendar_output($events, 'dental-appointment-' . (int)$appointment['id'] . '.ics', 'Dental appointment');
}

if (!in_array($type, ['doctor_week','doctor_month'], true)) { http_response_code(400); exit('Unsupported calendar export.'); }
require_login();
require_roles(['super_admin','clinic_admin','dentist']);

$branchId = (int)($_GET['branch_id'] ?? current_user()['branch_id'] ?? 0);
$doctorId = active_role_is('dentist') ? (int)(current_user()['id'] ?? 0) : (int)($_GET['doctor_id'] ?? 0);
if ($branchId < 1 || $doctorId < 1) throw new RuntimeException('Choose a valid branch and dentist.');
if (active_role_is('dentist') && $doctorId !== (int)(current_user()['id'] ?? 0)) access_denied('Dentists may export only their own calendar.');

$doctorStmt = $pdo->prepare('SELECT u.name,b.name branch_name,b.address branch_address,b.timezone FROM `' . table('users') . '` u LEFT JOIN `' . table('branches') . '` b ON b.id=? WHERE u.id=? AND u.status="active" LIMIT 1');
$doctorStmt->execute([$branchId,$doctorId]);
$doctor = $doctorStmt->fetch();
if (!$doctor) { http_response_code(404); exit('Dentist not found.'); }
$timezone = (string)($doctor['timezone'] ?: app_config('app.timezone','Asia/Manila'));

$anchor = trim((string)($_GET['date'] ?? date('Y-m-d')));
$anchorTs = strtotime($anchor) ?: time();
if ($type === 'doctor_month') {
    $rangeStart = date('Y-m-01', $anchorTs);
    $rangeEnd = date('Y-m-01', strtotime($rangeStart . ' +1 month'));
    $label = date('F Y', strtotime($rangeStart));
    $fileName = 'doctor-calendar-' . date('Y-m', strtotime($rangeStart)) . '.ics';
} else {
    $rangeStart = date('Y-m-d', strtotime('-' . ((int)date('N',$anchorTs)-1) . ' days', $anchorTs));
    $rangeEnd = date('Y-m-d', strtotime($rangeStart . ' +7 days'));
    $label = date('M j',strtotime($rangeStart)) . '–' . date('M j, Y',strtotime($rangeEnd . ' -1 day'));
    $fileName = 'doctor-calendar-week-' . $rangeStart . '.ics';
}

$ruleStmt = $pdo->prepare('SELECT * FROM `' . table('doctor_availability_rules') . '` WHERE doctor_user_id=? AND branch_id=? AND status="active" AND effective_from<? AND (effective_until IS NULL OR effective_until>=?) ORDER BY day_of_week,start_time');
$ruleStmt->execute([$doctorId,$branchId,$rangeEnd,$rangeStart]);
$rules = $ruleStmt->fetchAll();
$rulesByDay = [];
foreach ($rules as $rule) $rulesByDay[(int)$rule['day_of_week']][] = $rule;

$exceptionStmt = $pdo->prepare('SELECT * FROM `' . table('doctor_availability_exceptions') . '` WHERE doctor_user_id=? AND branch_id=? AND status="active" AND exception_date>=? AND exception_date<? ORDER BY exception_date,start_time');
$exceptionStmt->execute([$doctorId,$branchId,$rangeStart,$rangeEnd]);
$exceptions = $exceptionStmt->fetchAll();

$appointmentStmt = $pdo->prepare('SELECT a.*,CONCAT(p.first_name," ",p.last_name) patient FROM `' . table('appointments') . '` a JOIN `' . table('patients') . '` p ON p.id=a.patient_id WHERE a.dentist_user_id=? AND a.branch_id=? AND a.start_at>=? AND a.start_at<? AND a.status NOT IN ("cancelled","no_show") ORDER BY a.start_at');
$appointmentStmt->execute([$doctorId,$branchId,$rangeStart . ' 00:00:00',$rangeEnd . ' 00:00:00']);
$appointments = $appointmentStmt->fetchAll();

$events = [];
for ($date=$rangeStart; $date<$rangeEnd; $date=date('Y-m-d',strtotime($date . ' +1 day'))) {
    $day = (int)date('w',strtotime($date));
    foreach ($rulesByDay[$day] ?? [] as $rule) {
        if ($date < (string)$rule['effective_from'] || (!empty($rule['effective_until']) && $date > (string)$rule['effective_until'])) continue;
        $events[] = [
            'uid'=>'availability-' . (int)$rule['id'] . '-' . $date . '@' . dental_current_host(),
            'start'=>$date . ' ' . (string)$rule['start_time'],
            'end'=>$date . ' ' . (string)$rule['end_time'],
            'timezone'=>$timezone,
            'summary'=>'Clinic hours — ' . (string)$doctor['name'],
            'description'=>'Published DentalCare working hours.',
            'location'=>trim((string)($doctor['branch_name'] ?? '') . (($doctor['branch_address'] ?? '') ? ', ' . (string)$doctor['branch_address'] : '')),
            'status'=>'CONFIRMED',
        ];
    }
}
foreach ($exceptions as $exception) {
    $allDay = (bool)$exception['all_day'];
    $date = (string)$exception['exception_date'];
    $events[] = [
        'uid'=>'schedule-exception-' . (int)$exception['id'] . '@' . dental_current_host(),
        'start'=>$allDay ? $date : $date . ' ' . (string)$exception['start_time'],
        'end'=>$allDay ? date('Y-m-d',strtotime($date . ' +1 day')) : $date . ' ' . (string)$exception['end_time'],
        'all_day'=>$allDay,
        'timezone'=>$timezone,
        'summary'=>ucwords(str_replace('_',' ',(string)$exception['exception_type'])) . ' — ' . (string)$doctor['name'],
        'description'=>(string)($exception['reason'] ?: 'DentalCare schedule exception'),
        'location'=>(string)($doctor['branch_name'] ?? ''),
        'status'=>'CONFIRMED',
    ];
}
foreach ($appointments as $appointment) {
    $events[] = [
        'uid'=>'appointment-' . (int)$appointment['id'] . '@' . dental_current_host(),
        'start'=>(string)$appointment['start_at'],
        'end'=>(string)$appointment['end_at'],
        'timezone'=>$timezone,
        'summary'=>(string)$appointment['patient'] . ' — ' . (string)$appointment['service_name'],
        'description'=>'Patient appointment' . (!empty($appointment['chair_name']) ? "\nChair: " . (string)$appointment['chair_name'] : ''),
        'location'=>(string)($doctor['branch_name'] ?? ''),
        'status'=>'CONFIRMED',
    ];
}
calendar_output($events,$fileName,(string)$doctor['name'] . ' — ' . $label);
