<?php
require_once __DIR__ . "/../../bootstrap.php";
require_once __DIR__ . '/../../src/AdminAuth.php';
AdminAuth::requireLogin();

if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}

$pageTitle = "Liste des TFE";
require_once __DIR__ . '/../../src/Database.php';

// ── CSV Import (inline, submitted to this same page) ─────────────────────────
$importMessage = '';
$importErrors  = [];
$importResults = [];
$importDone    = false;

if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['csv_file'])) {
    error_log('[admin/index.php] IMPORT ENTRY | method=' . $_SERVER['REQUEST_METHOD'] . ' | content_type=' . ($_SERVER['CONTENT_TYPE'] ?? 'none') . ' | csv_file_error=' . ($_FILES['csv_file']['error'] ?? 'none') . ' | post_keys=' . implode(',', array_keys($_POST)));
    if (!isset($_POST['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
        $importErrors[] = "Erreur de sécurité : token invalide.";
    } else {
        $importedCount = 0;
        $skippedCount  = 0;
        try {
            $importDb  = new Database();
            $importPdo = $importDb->getPDO();

            if ($_FILES['csv_file']['error'] !== UPLOAD_ERR_OK) {
                throw new Exception("Erreur lors du téléversement du fichier.");
            }

            $handle = fopen($_FILES['csv_file']['tmp_name'], 'r');
            if (!$handle) throw new Exception("Impossible d'ouvrir le fichier CSV.");

            // Scan up to 8 rows looking for a header row with known column names.
            // Build colIdx[name] → position map; fall back to positional if header not found.
            // Matching uses prefix + variant logic so "contact.visible" matches "contact",
            // "promoteur·ice(s)" matches "promoteur", "Licence" matches "license", etc.
            $colIdx = null;
            $headerRowNum = 0;
            $knownHeaders = [
                'identifiant', 'titre', 'sous-titre', 'auteur', 'contact',
                'promoteur', 'lecteur', 'ulb', 'externe', 'format', 'année', 'ap', 'orientation', 'finalité',
                'mots-clés', 'synopsis', 'contexte', 'remarques', 'langue',
                'autorisation', 'licence', 'license', 'points', 'lien baiu',
            ];
            for ($scan = 0; $scan < 8; $scan++) {
                $hrow = fgetcsv($handle, 0, ',', '"', '');
                if ($hrow === false) break;
                $headerRowNum++;
                $normRow = array_map(fn($s) => strtolower(trim((string)$s)), $hrow);
                $hits = 0;
                $map  = [];
                $used = [];
                foreach ($knownHeaders as $h) {
                    foreach ($normRow as $pos => $cell) {
                        if (isset($used[$pos])) continue;
                        // Exact match
                        if ($cell === $h) { $hits++; $map[$h] = $pos; $used[$pos] = true; break; }
                        // Licence/License cross-match
                        if (($h === 'licence' && $cell === 'license') || ($h === 'license' && $cell === 'licence'))
                            { $hits++; $map[$h] = $pos; $used[$pos] = true; break; }
                        // Prefix match (for compound headers like "contact.visible")
                        $hlen = strlen($h);
                        if ($hlen >= 4 && str_starts_with($cell, $h)) {
                            // Avoid short prefixes matching unrelated words
                            if ($hlen >= 5 || $cell === $h) { $hits++; $map[$h] = $pos; $used[$pos] = true; break; }
                        }
                        // Substring match for short distinguishers (ulb, externe)
                        if ($hlen >= 3 && $hlen <= 7 && str_contains($cell, $h)) {
                            $hits++; $map[$h] = $pos; $used[$pos] = true; break;
                        }
                    }
                }
                // Require at least 11 known headers to trust the row.
                if ($hits >= 11) { $colIdx = $map; break; }
            }
            // If no header row found, rewind and fall back to positional (skip 4 rows).
            if ($colIdx === null) {
                rewind($handle);
                $headerRowNum = 4;
                for ($i = 0; $i < 4; $i++) fgetcsv($handle);
            } else {
                // Consume blank/instruction/template rows between header and data.
                // Stops when a row has a non-empty identifiant column that is not a
                // template placeholder (e.g. "Column1") or instruction snippet.
                $idPos = $colIdx['identifiant'] ?? 0;
                $peekRow = null;
                while (true) {
                    $peek = fgetcsv($handle, 0, ',', '"', '');
                    if ($peek === false) break;
                    $headerRowNum++;
                    $val = trim((string)($peek[$idPos] ?? ''));
                    if ($val === '' || str_starts_with(strtolower($val), 'column')
                                  || str_contains(strtolower($val), 'éparer')) {
                        continue; // metadata row, skip
                    }
                    $peekRow = $peek;
                    break;
                }
            }

            // Helper: get cell value by column name.
            // When header was found: only use mapped column (returns '' if missing from header).
            // When no header found: use positional fallback index.
            $cell = function(array $row, string $name, int $fallbackPos) use ($colIdx): string {
                if ($colIdx !== null) {
                    $pos = $colIdx[$name] ?? null;
                    if ($pos === null) {
                        // Try licence/license cross-lookup
                        if ($name === 'license') $pos = $colIdx['licence'] ?? null;
                        elseif ($name === 'licence') $pos = $colIdx['license'] ?? null;
                    }
                    if ($pos === null) return '';
                } else {
                    $pos = $fallbackPos;
                }
                return isset($row[$pos]) ? trim((string)$row[$pos]) : '';
            };

            // Code → canonical name (legacy short-code CSV format)
            $orientationCodeMap = [
                'SC'=>'Sculpture','VI'=>'Vidéographie','CA'=>"Cinéma d'animation",
                'IP'=>'Installation-Performance','PE'=>'Peinture','PH'=>'Photographie',
                'DE'=>'Dessin','AN'=>'Arts Numériques','GR'=>'Graphisme',
                'TY'=>'Typographie','DN'=>'Design Numérique','IL'=>'Illustration',
                'BD'=>'Bande-Dessinée','SE'=>'Sérigraphie','GV'=>'Gravure',
            ];

            // Alias map: normalise known variant spellings → canonical DB name.
            // Keys are lowercased+stripped for comparison.
            $orientationAliases = [
                'arts numériques'         => 'Arts Numériques',
                'design numérique'        => 'Design Numérique',
                'installation/performance' => 'Installation-Performance',
                'installation performance' => 'Installation-Performance',
                'cinema d\'animation'     => "Cinéma d'animation",
                'cinéma d\'animation'     => "Cinéma d'animation",
                'bande dessinée'          => 'Bande-Dessinée',
            ];

            // Resolve an orientation string (code or full name) → canonical DB name.
            $resolveOrientation = function(string $raw) use ($orientationCodeMap, $orientationAliases, $importPdo): ?int {
                $raw = trim($raw);
                if ($raw === '') return null;

                // 1. Try legacy short code
                if (isset($orientationCodeMap[$raw])) {
                    $raw = $orientationCodeMap[$raw];
                }

                // 2. Try alias map (lowercase key)
                $key = strtolower($raw);
                if (isset($orientationAliases[$key])) {
                    $raw = $orientationAliases[$key];
                }

                // 3. Exact DB match
                $s = $importPdo->prepare("SELECT id FROM orientations WHERE name = ?");
                $s->execute([$raw]);
                $r = $s->fetch();
                if ($r) return (int)$r['id'];

                // 4. Case-insensitive DB match
                $s = $importPdo->prepare("SELECT id FROM orientations WHERE LOWER(name) = LOWER(?)");
                $s->execute([$raw]);
                $r = $s->fetch();
                return $r ? (int)$r['id'] : null;
            };

            // AP alias map: variant spellings → canonical code.
            $apAliases = [
                'l.i.e.n.s.'                => 'LIENS',
                'liens'                     => 'LIENS',
                'lieux, interdisciplinarités, écologie, nécessité, systèmes'
                                            => 'LIENS',
                'récits et expérimentation' => 'NS',
                'recits et experimentation' => 'NS',
                'narraion spéculative'      => 'NS',
                'narration spéculative'     => 'NS',
                'atelier pratiques situées' => 'APS',
                'design & politique du multiple' => 'DPM',
                'design et politique du multiple' => 'DPM',
                'pacs'                      => 'PACS',
                'pratique de l\'art'          => 'PACS',
                'pratiques artistiques & complexité scientifique' => 'PACS',
            ];

            // Resolve an AP string (code or full name) → ap_program id.
            $resolveAP = function(string $raw) use ($apAliases, $importPdo): ?int {
                $raw = trim($raw);
                if ($raw === '') return null;

                // 1. Try alias map (lowercase key) → canonical code
                $key = strtolower($raw);
                if (isset($apAliases[$key])) {
                    $raw = $apAliases[$key];
                }

                // 2. Match by code
                $s = $importPdo->prepare("SELECT id FROM ap_programs WHERE code = ?");
                $s->execute([$raw]);
                $r = $s->fetch();
                if ($r) return (int)$r['id'];

                // 3. Code match (legacy)
                $s = $importPdo->prepare("SELECT id FROM ap_programs WHERE code = ?");
                $s->execute([$raw]);
                $r = $s->fetch();
                if ($r) return (int)$r['id'];

                // 4. Case-insensitive name match
                $s = $importPdo->prepare("SELECT id FROM ap_programs WHERE LOWER(name) = LOWER(?)");
                $s->execute([$raw]);
                $r = $s->fetch();
                return $r ? (int)$r['id'] : null;
            };

            $lineNumber = $headerRowNum;
            $usePeek     = isset($peekRow) && $peekRow !== null;
            while (true) {
                if ($usePeek) {
                    $row = $peekRow;
                    $usePeek = false;
                } else {
                    $row = fgetcsv($handle, 0, ',', '"', '');
                    if ($row === false) break;
                }
                $lineNumber++;
                if (empty($row[0]) && empty($row[1])) continue;
                try {
                    $importDb->beginTransaction();

                    $identifier      = $cell($row, 'identifiant',  0);
                    $title           = $cell($row, 'titre',        1);
                    $subtitle        = $cell($row, 'sous-titre',   2);
                    $authorsRaw      = $cell($row, 'auteur',       3);
                    $contact         = $cell($row, 'contact',      4);
                    // Normalise CSV artefacts: OUI/NON → empty (not a valid email)
                    if ($contact !== '' && in_array(strtoupper(trim($contact)), ['NON', 'OUI'], true)) {
                        $contact = '';
                    }
                    $supervisorsRaw       = $cell($row, 'promoteur',    5);
                    $lecteursInternesRaw  = $cell($row, 'lecteur',      6);  // first "lecteur" col = interne
                    $lecteursExternesRaw  = $cell($row, 'externe',      7);  // contains "externe"
                    $promoteursUlbRaw     = $cell($row, 'ulb',          8);  // contains "ulb"
                    $formatsRaw      = $cell($row, 'format',       9);
                    $yearRaw         = $cell($row, 'année',        10);
                    $year            = $yearRaw !== '' ? intval($yearRaw) : 0;
                    // Fallback: derive year from identifier (e.g. "2024-003" → 2024)
                    if ($year === 0 && $identifier !== '' && preg_match('/^(\d{4})-/', $identifier, $m)) {
                        $year = (int)$m[1];
                    }
                    $apCode          = $cell($row, 'ap',           11);
                    $orientationCode = $cell($row, 'orientation',  12);
                    $finalityName    = $cell($row, 'finalité',     13);
                    $keywordsRaw     = $cell($row, 'mots-clés',    14);
                    $synopsis        = $cell($row, 'synopsis',     15);
                    $context         = $cell($row, 'contexte',     16);
                    $remarks         = $cell($row, 'remarques',    17);
                    $languageRaw     = $cell($row, 'langue',       18);
                    $access          = $cell($row, 'autorisation', 19);
                    $license         = $cell($row, 'license',      20);
                    $juryPointsRaw   = $cell($row, 'points',       21);
                    $juryPoints      = $juryPointsRaw !== '' ? floatval($juryPointsRaw) : null;
                    $baiuLink        = $cell($row, 'lien baiu',    22);

                    if ($title === '' || $year === 0) {
                        $missing = [];
                        if ($title === '') $missing[] = 'titre';
                        if ($year === 0)   $missing[] = 'année';
                        throw new Exception("Champ(s) requis manquant(s) : " . implode(', ', $missing)
                            . " (id=\"" . ($identifier ?: '?') . "\", titre=\"" . substr($title, 0, 80) . "\")");
                    }

                    $orientationId = $resolveOrientation($orientationCode);
                    $apProgramId   = $resolveAP($apCode);

                    $finalityId = null;
                    if (!empty($finalityName)) {
                        $s = $importPdo->prepare("SELECT id FROM finality_types WHERE name = ?");
                        $s->execute([$finalityName]);
                        $r = $s->fetch(); $finalityId = $r ? $r['id'] : null;
                    }

                    $accessTypeId = null;
                    if (!empty($access)) {
                        $s = $importPdo->prepare("SELECT id FROM access_types WHERE name = ?");
                        $s->execute([ucfirst(strtolower($access))]);
                        $r = $s->fetch(); $accessTypeId = $r ? $r['id'] : null;
                    }
                    if ($accessTypeId === null) $accessTypeId = 1;

                    if (!empty($identifier)) {
                        $s = $importPdo->prepare("SELECT id FROM theses WHERE identifier = ?");
                        $s->execute([$identifier]);
                        if ($s->fetch()) {
                            $importDb->rollback();
                            $skippedCount++;
                            $importResults[] = ['type'=>'skip', 'msg'=>"Ligne $lineNumber: identifiant \"$identifier\" déjà présent, ignoré."];
                            continue;
                        }
                    }

                    $s = $importPdo->prepare("
                        INSERT INTO theses (
                            identifier, title, subtitle, year,
                            orientation_id, ap_program_id, finality_id,
                            synopsis, context_note, remarks,
                            jury_points, baiu_link,
                            access_type_id, is_published, submitted_at
                        ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,0,CURRENT_TIMESTAMP)
                    ");
                    $s->execute([
                        !empty($identifier) ? $identifier : null, $title,
                        !empty($subtitle)   ? $subtitle   : null, $year,
                        $orientationId, $apProgramId, $finalityId,
                        !empty($synopsis)  ? $synopsis  : null,
                        !empty($context)   ? $context   : null,
                        !empty($remarks)   ? $remarks   : null,
                        $juryPoints,
                        !empty($baiuLink)  ? $baiuLink  : null,
                        $accessTypeId,
                    ]);
                    $thesisId = $importPdo->lastInsertId();

                    if (!empty($authorsRaw)) {
                        foreach (array_map('trim', explode(',', $authorsRaw)) as $idx => $name) {
                            if ($name) {
                                $aId = $importDb->findOrCreateAuthor($name, $idx === 0 ? $contact : null);
                                $s = $importPdo->prepare("INSERT INTO thesis_authors (thesis_id, author_id, author_order) VALUES (?,?,?)");
                                $s->execute([$thesisId, $aId, $idx + 1]);
                            }
                        }
                    }
                    // Insert supervisors with proper role/is_external/is_ulb flags
                    $juryOrder = 0;
                    // Promoteurs internes
                    if (!empty($supervisorsRaw)) {
                        foreach (array_map('trim', explode(',', $supervisorsRaw)) as $name) {
                            if ($name) {
                                $juryOrder++;
                                $sId = $importDb->findOrCreateSupervisor($name);
                                $stmt = $importPdo->prepare("INSERT INTO thesis_supervisors (thesis_id, supervisor_id, role, is_external, is_ulb, supervisor_order) VALUES (?,?,?,?,?,?)");
                                $stmt->execute([$thesisId, $sId, 'promoteur', 0, 0, $juryOrder]);
                            }
                        }
                    }
                    // Lecteurs internes
                    if (!empty($lecteursInternesRaw)) {
                        foreach (array_map('trim', explode(',', $lecteursInternesRaw)) as $name) {
                            if ($name) {
                                $juryOrder++;
                                $sId = $importDb->findOrCreateSupervisor($name);
                                $stmt = $importPdo->prepare("INSERT INTO thesis_supervisors (thesis_id, supervisor_id, role, is_external, is_ulb, supervisor_order) VALUES (?,?,?,?,?,?)");
                                $stmt->execute([$thesisId, $sId, 'lecteur', 0, 0, $juryOrder]);
                            }
                        }
                    }
                    // Lecteurs externes
                    if (!empty($lecteursExternesRaw)) {
                        foreach (array_map('trim', explode(',', $lecteursExternesRaw)) as $name) {
                            if ($name) {
                                $juryOrder++;
                                $sId = $importDb->findOrCreateSupervisor($name);
                                $stmt = $importPdo->prepare("INSERT INTO thesis_supervisors (thesis_id, supervisor_id, role, is_external, is_ulb, supervisor_order) VALUES (?,?,?,?,?,?)");
                                $stmt->execute([$thesisId, $sId, 'lecteur', 1, 0, $juryOrder]);
                            }
                        }
                    }
                    // Promoteurs ULB
                    if (!empty($promoteursUlbRaw)) {
                        foreach (array_map('trim', explode(',', $promoteursUlbRaw)) as $name) {
                            if ($name) {
                                $juryOrder++;
                                $sId = $importDb->findOrCreateSupervisor($name);
                                $stmt = $importPdo->prepare("INSERT INTO thesis_supervisors (thesis_id, supervisor_id, role, is_external, is_ulb, supervisor_order) VALUES (?,?,?,?,?,?)");
                                $stmt->execute([$thesisId, $sId, 'promoteur', 1, 1, $juryOrder]);
                            }
                        }
                    }
                    if (!empty($keywordsRaw)) {
                        $normalizeTag = fn(string $t): string => strtolower(trim(preg_replace('/\s+/', ' ', $t)));
                        $tags = array_values(array_unique(array_map($normalizeTag, explode(',', $keywordsRaw))));
                        $tags = array_filter($tags, fn($t) => $t !== '');
                        foreach (array_slice($tags, 0, 10) as $kw) {
                            $tId = $importDb->findOrCreateTag($kw);
                            if ($tId) {
                                $s = $importPdo->prepare("INSERT INTO thesis_tags (thesis_id, tag_id) VALUES (?,?)");
                                $s->execute([$thesisId, $tId]);
                            }
                        }
                    }
                    if (!empty($languageRaw)) {
                        foreach (array_map('trim', explode(',', $languageRaw)) as $langName) {
                            $langName = strtolower($langName);
                            if ($langName === '') continue;
                            // Lookup case-insensitively; insert if missing (stored lowercase).
                            $s = $importPdo->prepare("SELECT id FROM languages WHERE LOWER(name) = LOWER(?)");
                            $s->execute([$langName]);
                            $r = $s->fetch();
                            $langId = $r ? (int)$r['id'] : null;
                            if ($langId === null) {
                                $importPdo->prepare("INSERT INTO languages (name) VALUES (?)")->execute([$langName]);
                                $langId = (int)$importPdo->lastInsertId();
                            }
                            $s2 = $importPdo->prepare("INSERT INTO thesis_languages (thesis_id, language_id) VALUES (?,?)");
                            $s2->execute([$thesisId, $langId]);
                        }
                    }
                    if (!empty($formatsRaw)) {
                        foreach (array_map('trim', explode(',', $formatsRaw)) as $fmt) {
                            if ($fmt) {
                                $s = $importPdo->prepare("SELECT id FROM format_types WHERE name = ?");
                                $s->execute([ucfirst(strtolower($fmt))]);
                                $r = $s->fetch();
                                if ($r) {
                                    $s2 = $importPdo->prepare("INSERT INTO thesis_formats (thesis_id, format_id) VALUES (?,?)");
                                    $s2->execute([$thesisId, $r['id']]);
                                }
                            }
                        }
                    }

                    $importDb->commit();
                    $importedCount++;
                    $importResults[] = ['type'=>'ok', 'msg'=>"\"$title\" (ID: $thesisId)"];

                } catch (Exception $e) {
                    $importDb->rollback();
                    $skippedCount++;
                    $importResults[] = ['type'=>'error', 'msg'=>"Ligne $lineNumber: " . $e->getMessage()];
                    error_log("Import error on line $lineNumber: " . $e->getMessage());
                }
            }
            fclose($handle);
            $importMessage = "Import terminé : $importedCount TFE importés, $skippedCount ignorés.";
            $importDone = true;
        } catch (Exception $e) {
            $importErrors[] = $e->getMessage();
            error_log("CSV import error: " . $e->getMessage());
        }
    }
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}

try {
    $db = new Database();
    $searchQuery       = isset($_GET['search'])      ? trim($_GET['search'])       : '';
    $yearFilter        = isset($_GET['year'])         ? intval($_GET['year'])        : null;
    $orientationFilter = isset($_GET['orientation']) ? intval($_GET['orientation']) : null;
    $apFilter          = isset($_GET['ap'])           ? intval($_GET['ap'])          : null;

    $sortCol  = isset($_GET['sort'])  ? trim($_GET['sort'])  : 'submitted_at';
    $sortDir  = isset($_GET['dir'])   ? trim($_GET['dir'])   : 'desc';

    $filters = [];
    if ($searchQuery)       $filters['search']      = $searchQuery;
    if ($yearFilter)        $filters['year']        = $yearFilter;
    if ($orientationFilter) $filters['orientation'] = $orientationFilter;
    if ($apFilter)          $filters['ap']          = $apFilter;
    $filters['sort'] = $sortCol;
    $filters['dir']  = $sortDir;

    $theses       = $db->getThesesList($filters, 0, 0);
    $totalCount   = count($theses);
    $stats        = $db->getThesesStats();
    $years        = $db->getAllYears();
    $orientations = $db->getAllOrientations();
    $apPrograms   = $db->getAllAPPrograms();
} catch (Exception $e) {
    error_log("Error loading theses list: " . $e->getMessage());
    die("Erreur lors du chargement de la liste.");
}

$isHtmx = ($_SERVER['HTTP_HX_REQUEST'] ?? '') === 'true';
$isAdmin = true; $bodyClass = 'admin-body';
if ($isHtmx) {
    include APP_ROOT . '/templates/admin/index-table.php';
} else {
    require_once APP_ROOT . '/templates/head.php';
    include APP_ROOT . '/templates/header.php';
    include APP_ROOT . '/templates/admin/index.php';
    require_once APP_ROOT . '/templates/admin/footer.php';
}

