<?php

declare(strict_types=1);

final class AgendaController
{
    public static function index(): void
    {
        $month = (string) ($_GET['month'] ?? date('Y-m'));
        if (!preg_match('/^\d{4}-\d{2}$/', $month)) {
            $month = date('Y-m');
        }
        $cursor =
            DateTimeImmutable::createFromFormat('!Y-m', $month) ?: new DateTimeImmutable('first day of this month');
        $date = (string) ($_GET['date'] ?? date('Y-m-d'));
        $weekCursor = self::date($date) ?: new DateTimeImmutable('today');
        $db = DB::pdo();
        $user = Auth::user();
        $requestedView = (string) ($_GET['view'] ?? '');
        $viewMode = in_array($requestedView, ['month', 'week'], true)
            ? $requestedView
            : (string) ($_SESSION['agenda_view'] ?? ($user['agenda_view'] ?? 'month'));
        if (!in_array($viewMode, ['month', 'week'], true)) {
            $viewMode = 'month';
        }
        if ($requestedView !== '') {
            $_SESSION['agenda_view'] = $viewMode;
        }
        $filters = self::filters();
        $items = self::filtered(
            $viewMode === 'week' ? AgendaService::week($weekCursor) : AgendaService::month($cursor),
            $filters,
            (int) $user['id'],
        );
        $upcoming = self::filtered(AgendaService::upcoming(), $filters, (int) $user['id']);
        $feed = $db->prepare('SELECT agenda_feed_token_hash,agenda_feed_created_at FROM users WHERE id=?');
        $feed->execute([(int) $user['id']]);
        $feedState = $feed->fetch(PDO::FETCH_ASSOC) ?: [];
        $token = (string) ($_SESSION['agenda_feed_token_once'] ?? '');
        unset($_SESSION['agenda_feed_token_once']);
        $volunteers = $db
            ->query(
                "SELECT DISTINCT dc.id,dc.name FROM directory_contacts dc JOIN directory_contact_roles r ON r.contact_id=dc.id AND r.role='benevole' WHERE dc.deleted_at IS NULL ORDER BY dc.name COLLATE NOCASE",
            )
            ->fetchAll(PDO::FETCH_ASSOC);
        $edit = (int) ($_GET['edit'] ?? 0);
        render('agenda.php', [
            'title' => t('agenda.title'),
            'cursor' => $cursor,
            'weekCursor' => $weekCursor,
            'viewMode' => $viewMode,
            'items' => $items,
            'upcoming' => $upcoming,
            'filters' => $filters,
            'filterSuffix' => self::filterSuffix($filters),
            'feedEnabled' => !empty($feedState['agenda_feed_token_hash']),
            'feedCreatedAt' => $feedState['agenda_feed_created_at'] ?? null,
            'feedUrl' => $token !== '' ? CalendarFeedService::absoluteUrl('/agenda/feed.ics?token=' . $token) : '',
            'editItem' => $edit ? AgendaService::find($edit) : null,
            'animals' => $db
                ->query(
                    'SELECT id,name FROM animals WHERE deleted_at IS NULL AND archived_at IS NULL ORDER BY name COLLATE NOCASE',
                )
                ->fetchAll(),
            'contacts' => $db
                ->query('SELECT id,name FROM directory_contacts WHERE deleted_at IS NULL ORDER BY name COLLATE NOCASE')
                ->fetchAll(),
            'volunteers' => $volunteers,
            'users' => $db
                ->query(
                    "SELECT id,COALESCE(NULLIF(display_name,''),username) name FROM users WHERE active=1 ORDER BY name COLLATE NOCASE",
                )
                ->fetchAll(),
            'status' => (string) ($_GET['status'] ?? ''),
            'error' => (string) ($_GET['error'] ?? ''),
        ]);
    }
    public static function save(): void
    {
        try {
            $id = (int) ($_POST['id'] ?? 0);
            $saved = AgendaService::save($_POST, $id ?: null);
            $conflicts = AgendaService::conflicts($saved);
            AuditService::log(
                $id ? 'agenda_updated' : 'agenda_created',
                '/agenda/save',
                $id ? 'Événement modifié' : 'Événement créé',
                'agenda',
                $saved,
                ['conflicts' => count($conflicts)],
            );
            header('Location: /agenda?status=' . ($conflicts ? 'conflict' : 'saved'));
        } catch (Throwable $e) {
            header('Location: /agenda?error=' . rawurlencode(SecurityService::publicError($e)));
        }
        exit();
    }
    public static function status(): void
    {
        try {
            $id = (int) ($_POST['id'] ?? 0);
            AgendaService::setStatus($id, (string) ($_POST['status'] ?? ''));
            AuditService::log('agenda_status', '/agenda/status', 'État de l’agenda modifié', 'agenda', $id, [
                'status' => $_POST['status'] ?? '',
            ]);
            header('Location: /agenda?status=updated');
        } catch (Throwable $e) {
            header('Location: /agenda?error=' . rawurlencode(SecurityService::publicError($e)));
        }
        exit();
    }
    public static function export(): void
    {
        $view = (string) ($_GET['view'] ?? 'month');
        if ($view === 'week') {
            $date = self::date((string) ($_GET['date'] ?? '')) ?: new DateTimeImmutable('today');
            $items = AgendaService::week($date);
            $name = 'agenda-semaine-' . $date->modify('monday this week')->format('Y-m-d') . '.ics';
        } else {
            $month = (string) ($_GET['month'] ?? date('Y-m'));
            $date = preg_match('/^\d{4}-\d{2}$/', $month) ? DateTimeImmutable::createFromFormat('!Y-m', $month) : false;
            $date = $date ?: new DateTimeImmutable('first day of this month');
            $items = AgendaService::month($date);
            $name = 'agenda-' . $date->format('Y-m') . '.ics';
        }
        $items = self::filtered($items, self::filters(), (int) Auth::id());
        self::output(
            CalendarFeedService::render($items, AppSettings::get('association_name') . ' — ' . t('agenda.title')),
            $name,
        );
    }
    public static function feed(): void
    {
        $user = CalendarFeedService::userForToken((string) ($_GET['token'] ?? ''));
        if (!$user) {
            http_response_code(404);
            header('Content-Type: text/plain; charset=utf-8');
            echo t('agenda.feed_invalid');
            return;
        }
        $items = AgendaService::range(new DateTimeImmutable('-90 days'), new DateTimeImmutable('+365 days 23:59:59'));
        self::output(
            CalendarFeedService::render($items, AppSettings::get('association_name') . ' — ' . t('agenda.title')),
            'globinours-agenda.ics',
            false,
        );
    }
    public static function regenerateFeed(): void
    {
        self::feedPost();
        $_SESSION['agenda_feed_token_once'] = CalendarFeedService::generateToken((int) Auth::id());
        AuditService::log('agenda_feed_regenerated', '/agenda/feed/regenerate', 'Lien privé d’agenda régénéré');
        header('Location: /agenda?status=feed');
        exit();
    }
    public static function revokeFeed(): void
    {
        self::feedPost();
        CalendarFeedService::revoke((int) Auth::id());
        AuditService::log('agenda_feed_revoked', '/agenda/feed/revoke', 'Lien privé d’agenda révoqué');
        header('Location: /agenda?status=feed-revoked');
        exit();
    }
    private static function filters(): array
    {
        $types = [...AgendaService::TYPES, 'vaccine', 'deworming', 'treatment_start', 'treatment_end'];
        $scope = ($_GET['scope'] ?? 'all') === 'mine' ? 'mine' : 'all';
        $type = in_array($t = (string) ($_GET['category'] ?? ''), $types, true) ? $t : '';
        return [
            'scope' => $scope,
            'assigned_user_id' => max(0, (int) ($_GET['assigned_user_id'] ?? 0)),
            'volunteer_contact_id' => max(0, (int) ($_GET['volunteer_contact_id'] ?? 0)),
            'category' => $type,
            'show_medical' => (string) ($_GET['show_medical'] ?? '1') !== '0',
            'q' => mb_substr(trim((string) ($_GET['q'] ?? '')), 0, 100),
        ];
    }
    private static function filtered(array $items, array $f, int $userId): array
    {
        return array_values(
            array_filter($items, static function ($row) use ($f, $userId): bool {
                $source = (string) ($row['source_type'] ?? 'agenda');
                if ($f['scope'] === 'mine' && (int) ($row['assigned_user_id'] ?? 0) !== $userId) {
                    return false;
                }
                if ($f['assigned_user_id'] && (int) ($row['assigned_user_id'] ?? 0) !== $f['assigned_user_id']) {
                    return false;
                }
                if (
                    $f['volunteer_contact_id'] &&
                    !in_array(
                        $f['volunteer_contact_id'],
                        array_map('intval', array_filter(explode(',', (string) ($row['volunteer_ids'] ?? '')))),
                        true,
                    )
                ) {
                    return false;
                }
                if ($f['category'] !== '' && (string) $row['item_type'] !== $f['category']) {
                    return false;
                }
                if (!$f['show_medical'] && $source !== 'agenda') {
                    return false;
                }
                if (
                    $f['q'] !== '' &&
                    !str_contains(
                        mb_strtolower(
                            implode(' ', [
                                (string) ($row['title'] ?? ''),
                                (string) ($row['animal_name'] ?? ''),
                                (string) ($row['location'] ?? ''),
                                (string) ($row['assigned_name'] ?? ''),
                                (string) ($row['volunteer_names'] ?? ''),
                            ]),
                        ),
                        mb_strtolower($f['q']),
                    )
                ) {
                    return false;
                }
                return true;
            }),
        );
    }
    private static function filterSuffix(array $f): string
    {
        $query = http_build_query(
            [
                'scope' => $f['scope'],
                'assigned_user_id' => $f['assigned_user_id'] ?: null,
                'volunteer_contact_id' => $f['volunteer_contact_id'] ?: null,
                'category' => $f['category'] ?: null,
                'show_medical' => $f['show_medical'] ? '1' : '0',
                'q' => $f['q'] ?: null,
            ],
            '',
            '&',
            PHP_QUERY_RFC3986,
        );
        return $query !== '' ? '&amp;' . str_replace('&', '&amp;', $query) : '';
    }
    private static function feedPost(): void
    {
        if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST' || !Auth::validCsrf($_POST['csrf'] ?? null)) {
            http_response_code(419);
            exit();
        }
        if (!Auth::user() || !PermissionService::can('agenda')) {
            http_response_code(403);
            exit();
        }
    }
    private static function output(string $content, string $filename, bool $attachment = true): void
    {
        header('Content-Type: text/calendar; charset=utf-8');
        header('Content-Disposition: ' . ($attachment ? 'attachment' : 'inline') . '; filename="' . $filename . '"');
        header('Cache-Control: private, no-store');
        echo $content;
        exit();
    }
    private static function date(string $value): ?DateTimeImmutable
    {
        $date = DateTimeImmutable::createFromFormat('!Y-m-d', $value);
        return $date && $date->format('Y-m-d') === $value ? $date : null;
    }
}
