<?php
/**
 * TfeController
 *
 * Handles all data-fetching and view-variable assembly for the public TFE
 * detail page (public/tfe.php).
 *
 * Responsibilities:
 *  - Validate the `id` GET parameter and load the thesis record
 *  - Enforce publication visibility (redirect to index on 404)
 *  - Resolve the OG image (banner → first image file)
 *  - Build the complete OG / Twitter Card tag array
 *  - Assemble the meta description from the synopsis
 *  - Collect WebVTT caption file paths for video pairing
 *  - Return a flat array of view variables ready for template extraction
 *
 * The class has NO output side-effects; all template rendering stays in
 * public/tfe.php so the view layer remains easy to inspect and modify.
 */
class TfeController
{
    private const BASE_URL     = 'https://posterg.erg.be';
    private const META_MAX_LEN = 160;

    private Database $db;

    public function __construct(Database $db)
    {
        $this->db = $db;
    }

    // ── Factory ───────────────────────────────────────────────────────────────

    /**
     * Convenience factory: loads the Database singleton and returns a ready
     * controller instance.
     */
    public static function create(): self
    {
        require_once APP_ROOT . '/src/Database.php';

        return new self(Database::getInstance());
    }

    // ── Main entry point ─────────────────────────────────────────────────────

    /**
     * Process the current request.
     *
     * On success returns an array of view variables.
     * On failure (missing id, thesis not found) sends a redirect and exits.
     *
     * @return array<string, mixed>
     */
    public function handle(): array
    {
        $thesisId = isset($_GET['id']) ? (int) $_GET['id'] : 0;

        if ($thesisId <= 0) {
            $this->redirectHome();
        }

        try {
            $data = $this->db->getThesisById($thesisId);
        } catch (Exception $e) {
            error_log('TfeController: ' . $e->getMessage());
            $this->redirectHome();
        }

        if (empty($data)) {
            $this->redirectHome();
        }

        // Access type (1 = open, 2 = restricted, 3 = forbidden)
        $accessTypeId = $this->db->getThesisAccessTypeId($thesisId) ?? 1;
        $isInterdit   = ($accessTypeId === 3);

        // Caption (WebVTT) files — N-th VTT is paired with the N-th <video>
        $captionFiles = $this->collectCaptionPaths($data['files'] ?? []);

        // Jury members with interne/externe split
        $jury = $this->db->getThesisJury($thesisId);
        $juryByRole = $this->splitJuryByRole($jury);

        // Page meta
        $metaDescription = $this->buildMetaDescription($data['synopsis'] ?? '');
        $ogTags          = $this->buildOgTags($data, $thesisId, $metaDescription);
        $pageTitle       = $data['title']
            . (!empty($data['authors']) ? ' – ' . $data['authors'] : '')
            . ' – Posterg';

        return [
            // Core data
            'thesisId'          => $thesisId,
            'data'              => $data,
            'accessTypeId'      => $accessTypeId,
            'isInterdit'        => $isInterdit,
            'captionFiles'      => $captionFiles,
            'juryPresidents'    => $juryByRole['presidents'],
            'promoteursInternes' => $juryByRole['internes'],
            'promoteursExternes' => $juryByRole['externes'],
            'juryLecteurs'      => $juryByRole['lecteurs'],

            // Page meta
            'pageTitle'       => $pageTitle,
            'metaDescription' => $metaDescription,
            'ogTags'          => $ogTags,

            // Layout
            'currentNav'  => '',
            'extraCss'    => ['/assets/css/tfe.css'],
            'bodyClass'   => 'tfe-body',
        ];
    }

    // ── Private helpers ───────────────────────────────────────────────────────

    /**
     * Build a ~160-character meta description from the thesis synopsis.
     */
    private function buildMetaDescription(string $synopsis): string
    {
        $plain = strip_tags($synopsis);

        if (empty($plain)) {
            return 'Mémoire de fin d\'études – Posterg, répertoire des TFE de l\'erg.';
        }

        return strlen($plain) > self::META_MAX_LEN
            ? substr($plain, 0, self::META_MAX_LEN - 3) . '…'
            : $plain;
    }

    /**
     * Resolve the OG image URL: banner_path → first image file → empty string.
     *
     * @param  array<int, array<string, mixed>> $files
     */
    private function resolveOgImage(array $files, ?string $bannerPath): string
    {
        if (!empty($bannerPath)) {
            return self::BASE_URL . '/media.php?path=' . rawurlencode($bannerPath);
        }

        foreach ($files as $file) {
            $ext = strtolower(pathinfo($file['file_path'] ?? '', PATHINFO_EXTENSION));
            if (in_array($ext, ['jpg', 'jpeg', 'png', 'gif', 'webp'], true)) {
                return self::BASE_URL . '/media.php?path=' . rawurlencode($file['file_path']);
            }
        }

        return '';
    }

    /**
     * Build the complete $ogTags array consumed by templates/head.php.
     *
     * @param  array<string, mixed> $data
     * @return array<string, string>
     */
    private function buildOgTags(array $data, int $thesisId, string $metaDescription): array
    {
        $ogImage  = $this->resolveOgImage($data['files'] ?? [], $data['banner_path'] ?? null);
        $title    = $data['title'] . (!empty($data['authors']) ? ' – ' . $data['authors'] : '');
        $imageAlt = $data['title'] . (!empty($data['authors']) ? ' par ' . $data['authors'] : '');

        return [
            'type'                   => 'article',
            'title'                  => $title,
            'description'            => $metaDescription,
            'url'                    => self::BASE_URL . '/tfe?id=' . $thesisId,
            'image'                  => $ogImage,
            'image_alt'              => $imageAlt,
            'site_name'              => 'Posterg – ERG',
            'article_author'         => $data['authors'] ?? '',
            'article_published_time' => !empty($data['year']) ? $data['year'] . '-01-01' : '',
        ];
    }

    /**
     * Split jury members by role and internal/external flag.
     *
     * @param  array<int, array<string, mixed>> $jury
     * @return array{presidents: list<string>, internes: list<string>, externes: list<string>, lecteurs: list<string>}
     */
    private function splitJuryByRole(array $jury): array
    {
        $result = ['presidents' => [], 'internes' => [], 'externes' => [], 'lecteurs' => []];

        foreach ($jury as $member) {
            $name = $member['name'] ?? '';
            if ($name === '') continue;

            switch ($member['role']) {
                case 'president':
                    $result['presidents'][] = $name;
                    break;
                case 'promoteur':
                    if ((int)$member['is_external'] === 1) {
                        $result['externes'][] = $name;
                    } else {
                        $result['internes'][] = $name;
                    }
                    break;
                case 'lecteur':
                    $result['lecteurs'][] = $name;
                    break;
            }
        }

        return $result;
    }

    /**
     * Return an ordered list of VTT caption file paths from the files array.
     * The N-th entry corresponds to the N-th <video> element in document order.
     *
     * @param  array<int, array<string, mixed>> $files
     * @return list<string>
     */
    private function collectCaptionPaths(array $files): array
    {
        $captions = [];

        foreach ($files as $file) {
            $mime = $file['mime_type'] ?? '';
            $ext  = strtolower(pathinfo($file['file_path'] ?? '', PATHINFO_EXTENSION));

            if ($mime === 'text/vtt' || $ext === 'vtt') {
                $captions[] = $file['file_path'];
            }
        }

        return $captions;
    }

    // ── Response helpers ──────────────────────────────────────────────────────

    /**
     * Redirect to the home page and terminate.  Never returns.
     */
    private function redirectHome(): never
    {
        header('Location: /');
        exit;
    }
}
