import { type StatusTone } from '@ema-platform/shared';
import { useState, useEffect, useRef, useMemo } from "react";
import { useNavigate, useParams } from "react-router-dom";
import {
  Stack,
  Title,
  Group,
  Text,
  Paper,
  Badge,
  SimpleGrid,
  Divider,
  Button,
  ActionIcon,
  Alert,
  Loader,
  Center,
  Modal,
  Table,
  Select,
  NumberInput,
  TextInput,
  ThemeIcon,
  Box,
  Tooltip,
  rem,
} from "@mantine/core";
import { useDisclosure } from "@mantine/hooks";
import { useTranslation } from "react-i18next";
import {
  IconArrowLeft,
  IconPrinter,
  IconPlus,
  IconInfoCircle,
  IconCertificate,
  IconCalendar,
  IconMapPin,
  IconClock,
  IconScoreboard,
  IconUser,
  IconCheck,
  IconX,
} from '@tabler/icons-react';
import { StatusBadge, ModalFooter, notify, useErrorHandler } from '@ema-platform/ui';
import { extractErrorMessage } from '@ema-platform/api';
import { LICENSE_PERMISSIONS, RequirePermission } from '@ema-platform/auth';
import {
  useGetExamQuery,
  useUpdateExamMutation,
  useAssignQuestionsMutation,
  useSelectRandomQuestionsMutation,
  useGetExamRegistrationsQuery,
} from '../api/exam-api';
import { useGetQuestionsQuery } from '../../question/api/question-api';
import { useGetCertificationsQuery } from '../../certification/api/certification-api';
import { QuestionAssigner } from '../components/QuestionAssigner';
import { RecordResultModal } from '../../result/components/RecordResultModal';
import { ExamCandidatesPanel } from '../components/ExamCandidatesPanel';
import { ExamIncidentsPanel } from '../components/ExamIncidentsPanel';
import { PageLoader } from '@ema-platform/ui';
import type { ExamStatus, QuestionBrief } from '../types/exam';

const STATUS_TONE: Record<string, StatusTone> = {
  PENDING: 'neutral',
  ACTIVE: 'info',
  COMPLETED: 'success',
  CANCELLED: 'danger',
  POSTPONED: 'pending',
  PUBLISHED: 'success',
};

const FORM_LABEL: Record<string, string> = {
  ESSAY: "Essay",
  CHOICE: "Choice",
  BOTH: "Both",
};
const TYPE_LABEL: Record<string, string> = { WRITTEN: "Written", ORAL: "Oral" };
const ADMIN_LABEL: Record<string, string> = {
  OFFLINE: "Offline",
  ONLINE: "Online",
};
const EVAL_LABEL: Record<string, string> = {
  SUM: "Sum",
  AVERAGE: "Average",
  PERCENTAGE: "Percentage",
};

function InfoRow({ label, value }: { label: string; value: string }) {
  return (
    <div>
      <Text fz="xs" c="dimmed" tt="uppercase" fw={600}>
        {label}
      </Text>
      <Text fz="sm" fw={500}>
        {value || "—"}
      </Text>
    </div>
  );
}

export function ExamDetailPage() {
  const { t, i18n } = useTranslation();
  const locale = i18n.language as "en" | "am";
  const { handleError } = useErrorHandler();
  const { id } = useParams<{ id: string }>();
  const navigate = useNavigate();
  const printRef = useRef<HTMLDivElement>(null);
  const [recordOpened, { open: openRecord, close: closeRecord }] =
    useDisclosure(false);
  const [assignOpened, { open: openAssign, close: closeAssign }] =
    useDisclosure(false);
  const [draftQuestions, setDraftQuestions] = useState<QuestionBrief[]>([]);
  const [randomCount, setRandomCount] = useState(5);
  const [updateExam] = useUpdateExamMutation();
  const [assignQuestions, { isLoading: isAssigning }] = useAssignQuestionsMutation();
  const [selectRandom, { isLoading: isDrawing }] = useSelectRandomQuestionsMutation();

  const { data: exam, isLoading, isError } = useGetExamQuery(id ?? '', { skip: !id });
  // The backend locks the paper the moment the first candidate registers
  // (ExamService.assertPaperEditable) — every candidate must sit the same
  // paper. Same query ExamCandidatesPanel already runs, so RTK Query serves
  // it from cache rather than issuing a second request.
  const { data: registrations } = useGetExamRegistrationsQuery(id ?? '', { skip: !id });
  const paperLocked = (registrations?.length ?? 0) > 0;
  const { data: qRes } = useGetQuestionsQuery();
  const { data: certRes } = useGetCertificationsQuery();
  const allQuestions = qRes?.items ?? [];
  const certifications = certRes?.items ?? [];

  // Only approved bank items may go on a paper (US-EXAM-003), so the picker
  // must not offer drafts or retired questions either.
  //
  // Filters on exam.form alone, not administrationMethod: the backend no
  // longer restricts ONLINE to CHOICE (ExamService no longer has an
  // assertOnlineIsChoiceOnly gate), so exam.form is now the sole source of
  // truth for what belongs on the paper, ONLINE or OFFLINE alike. BOTH
  // describes a mixed paper — a question itself is never "BOTH" (see
  // QuestionForm), so an equality check against it would match nothing and
  // silently offer zero questions; skipped the same way the backend's own
  // random draw does (ExamService.selectRandomQuestions).
  const eligibleQuestions = useMemo(() => {
    if (!exam) return [];
    return allQuestions
      .filter(
        (q) =>
          q.certificationId === exam.certificationId &&
          (exam.form === 'BOTH' || q.form === exam.form) &&
          q.status === 'APPROVED',
      )
      .map((q) => ({ id: q.id, title: q.title, form: q.form, points: q.points }));
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [allQuestions, exam?.certificationId, exam?.form]);

  if (isLoading)
    return <PageLoader label="Loading Exam Details…" height={400} />;
  if (isError || !exam) {
    return (
      <Stack gap="md">
        <Button
          variant="subtle"
          leftSection={<IconArrowLeft size={15} />}
          w="fit-content"
          onClick={() => navigate("/exams")}
        >
          {t("exam.backToExams")}
        </Button>
        <Alert color="red" icon={<IconInfoCircle size={17} />}>
          {t("exam.notFound")}
        </Alert>
      </Stack>
    );
  }

  const openAssignModal = () => {
    setDraftQuestions(exam.questions ?? []);
    setRandomCount(5);
    openAssign();
  };

  /**
   * The draw happens on the server (US-EXAM-005): it picks only approved
   * items for this subject and writes the paper in one call, so the pool is
   * never shipped to the browser and cannot be reshuffled until it flatters.
   */
  const handleRandomSelect = async () => {
    try {
      const updated = await selectRandom({ examId: exam.id, count: randomCount }).unwrap();
      setDraftQuestions(updated.questions ?? []);
      notify.success(t('exam.randomSelected', { count: (updated.questions ?? []).length }));
      closeAssign();
    } catch (error) {
      const key = extractErrorMessage(error, t('exam.randomError'));
      notify.error(
        key === 'paper_locked_after_registration'
          ? t('exam.paperLockedHint', { count: registrations?.length ?? 0 })
          : key.startsWith('insufficient_approved_questions')
          ? `${t('exam.notEnoughApproved')} (${key.split(':')[1] ?? ''})`
          : key.startsWith('paper_cannot_reach_cutting_point')
            ? t('exam.cannotReachCuttingPoint', {
                max: key.split(':')[1]?.split('/')[0] ?? '',
                cuttingPoint: key.split(':')[1]?.split('/')[1] ?? '',
              })
            : key,
      );
    }
  };

  const handleAssign = async () => {
    try {
      const questionIds = draftQuestions.map((q) => q.id);
      await assignQuestions({ examId: exam.id, questionIds, remark: undefined }).unwrap();
      notify.success('Questions assigned');
      closeAssign();
    } catch (error) {
      const key = extractErrorMessage(error, 'Failed to assign questions');
      notify.error(
        key === 'paper_locked_after_registration'
          ? t('exam.paperLockedHint', { count: registrations?.length ?? 0 })
          : key.startsWith('question_not_approved')
          ? t('question.qc.onlyApprovedUsable')
          : key.startsWith('paper_cannot_reach_cutting_point')
            ? t('exam.cannotReachCuttingPoint', {
                max: key.split(':')[1]?.split('/')[0] ?? '',
                cuttingPoint: key.split(':')[1]?.split('/')[1] ?? '',
              })
            : key,
      );
    }
  };
  const handlePrint = async () => {
    // The reachable max depends on the evaluation method, not the raw point
    // sum — mirrors RecordResultModal's grading math so "can this paper pass"
    // means the same thing here as it does at marking time. Cutting point can
    // be raised after the paper was assembled (edit modal, no re-check on
    // save), so this still needs to run even though assignment now enforces
    // it too.
    const questions = exam.questions ?? [];
    const total = questions.reduce((s, q) => s + Number(q.points), 0);
    const reachableMax =
      exam.evaluationMethod === 'AVERAGE'
        ? questions.length
          ? total / questions.length
          : 0
        : exam.evaluationMethod === 'PERCENTAGE'
          ? 100
          : total;
    if (reachableMax < Number(exam.cuttingPoint)) {
      notify.error(
        `This paper cannot reach the passing mark under its ${EVAL_LABEL[exam.evaluationMethod] ?? exam.evaluationMethod} evaluation (max ${reachableMax}, pass mark ${exam.cuttingPoint}). Add more questions or adjust the cutting point before printing.`,
      );
      return;
    }

    const printWindow = window.open("", "_blank");
    if (!printWindow) return;

    let logoBase64 = "";
    try {
      const resp = await fetch("/ema-logo.png");
      const blob = await resp.blob();
      logoBase64 = await new Promise<string>((resolve) => {
        const reader = new FileReader();
        reader.onloadend = () => resolve(reader.result as string);
        reader.readAsDataURL(blob);
      });
    } catch {
      /* logo not available */
    }

    const qMap = new Map(allQuestions.map((qq) => [qq.id, qq]));
    const qHtml = (exam.questions ?? [])
      .map((q, i) => {
        const full = qMap.get(q.id);
        const titleStr = q.title[locale] || q.title.en;
        const descStr =
          full?.description?.[locale] || full?.description?.en || "";
        return `
      <div style="margin-bottom: 24px; page-break-inside: avoid;">
        <p style="font-weight: 700; margin-bottom: 4px; font-size: 13px;">Question ${i + 1} (${q.points} pts — ${FORM_LABEL[q.form] ?? q.form})</p>
        <p style="margin: 0 0 4px 0; font-size: 14px; line-height: 1.5;">${titleStr}</p>
        ${descStr ? `<p style="margin: 0 0 8px 0; font-size: 12px; color: #555; line-height: 1.4;">${descStr}</p>` : ""}
        ${q.form === "ESSAY" ? '<div style="border-bottom: 1px dashed #ccc; height: 80px; margin-bottom: 12px;"></div>'.repeat(3) : ""}
        ${
          q.form === "CHOICE"
            ? q.options && q.options.length
              ? q.options
                  .slice()
                  .sort((a, b) => a.order - b.order)
                  .map(
                    (o, oi) =>
                      `<p style="margin: 4px 0; font-size: 13px;">${String.fromCharCode(65 + oi)}. ${o.text[locale] || o.text.en}</p>`,
                  )
                  .join("")
              // No options on record (legacy question, or options relation
              // wasn't loaded) — fall back to blank lines rather than
              // printing nothing.
              : ["A. ______", "B. ______", "C. ______", "D. ______"].map((l) => `<p style="margin: 4px 0; font-size: 13px;">${l}</p>`).join("")
            : ""
        }
      </div>`;
      })
      .join("");

    printWindow.document.write(`
      <html><head><title>${exam.title[locale] || exam.title.en}</title>
      <style>
        body { font-family: sans-serif; padding: 40px; max-width: 800px; margin: auto; }
        .header { text-align: center; margin-bottom: 32px; border-bottom: 2px solid #333; padding-bottom: 16px; }
        .header-logo { max-width: 80px; margin-bottom: 8px; }
        .header h1 { font-size: 20px; margin: 0 0 4px; }
        .header p { margin: 2px 0; font-size: 13px; color: #555; }
        .directions { background: #f5f5f5; padding: 12px 16px; border-radius: 4px; margin-bottom: 24px; font-size: 13px; }
        .directions strong { display: block; margin-bottom: 4px; }
        .footer { margin-top: 40px; border-top: 1px solid #ccc; padding-top: 12px; font-size: 12px; color: #888; text-align: center; }
        /* Pinned to the bottom of every printed page (not just after the
           last question) — @page's bottom margin leaves room for it so it
           never overlaps question text on the last page. */
        @media print {
          @page { margin: 20mm 20mm 28mm 20mm; }
          /* @page's margin already insets content from the physical page
             edge — body's own 40px padding (needed on-screen, for the
             preview tab before printing) would double up with it here,
             wasting real page height on every side and fitting noticeably
             fewer questions per page than the paper actually has room for. */
          body { -webkit-print-color-adjust: exact; padding: 0; max-width: none; }
          .footer { position: fixed; bottom: 0; left: 0; right: 0; margin-top: 0; }
        }
      </style></head><body>
      <div class="header">
        ${logoBase64 ? `<img src="${logoBase64}" alt="Logo" class="header-logo" />` : ""}
        <h1>${exam.title[locale] || exam.title.en}</h1>
        <p>Date: ${exam.date} | Venue: ${exam.venue}</p>
        <p>Form: ${FORM_LABEL[exam.form]} | Type: ${TYPE_LABEL[exam.type]} | Time Allowed: ${exam.givenTime ? `${exam.givenTime.days}d ${exam.givenTime.hours}h ${exam.givenTime.minutes}m` : "N/A"}</p>
        <p>Pass Mark: ${exam.cuttingPoint} | Total Questions: ${(exam.questions ?? []).length}</p>
      </div>
      ${exam.direction?.[locale] ? `<div class="directions"><strong>Directions:</strong> ${exam.direction[locale]}</div>` : ""}
      ${qHtml}
      <div class="footer">
        Generated by EMA — Ethiopian Maritime Authority
      </div>
      </body></html>
    `);
    printWindow.document.close();
    printWindow.focus();
    setTimeout(() => printWindow.print(), 500);
  };

  const totalPoints = (exam.questions ?? []).reduce(
    (s, q) => s + Number(q.points),
    0,
  );
  const certName =
    exam.certification?.name?.[locale] ??
    certifications.find((c) => c.id === exam.certificationId)?.name?.[locale] ??
    "—";

  return (
    <Stack gap="md" ref={printRef}>
      {/* Header */}
      <Group justify="space-between">
        <Group gap="sm">
          <ActionIcon
            variant="subtle"
            size="lg"
            onClick={() => navigate("/exams")}
          >
            <IconArrowLeft size={18} />
          </ActionIcon>
          <div>
            <Title order={2}>{exam.title[locale]}</Title>
          </div>
        </Group>
        <Group gap="sm">
          <Button
            variant="light"
            leftSection={<IconPrinter size={15} />}
            onClick={handlePrint}
            size="sm"
          >
            {t("exam.print")}
          </Button>
          <Button
            leftSection={<IconPlus size={15} />}
            onClick={openRecord}
            size="sm"
          >
            {t("exam.recordResult")}
          </Button>
        </Group>
      </Group>

      {/* Status badge */}
      <StatusBadge
        tone={STATUS_TONE[exam.status]}
        label={t(`exam.status.${exam.status}`)}
        size="lg"
        variant="light"
        style={{ width: "fit-content" }}
      />

      {/* Exam Info */}
      <Paper withBorder radius="lg" p="lg">
        <Title order={5} mb="md">
          {t("exam.detail.title")}
        </Title>
        <SimpleGrid cols={{ base: 2, sm: 4 }} spacing="md">
          <InfoRow label={t("exam.detail.certification")} value={certName} />
          <InfoRow
            label={t("exam.detail.type")}
            value={t(`exam.type.${exam.type}`)}
          />
          <InfoRow
            label={t("exam.detail.form")}
            value={t(`exam.formType.${exam.form}`)}
          />
          <InfoRow label={t("exam.detail.venue")} value={exam.venue} />
          <InfoRow label={t("exam.detail.date")} value={exam.date} />
          <InfoRow
            label={t("exam.detail.administration")}
            value={t(`exam.admin.${exam.administrationMethod}`)}
          />
          <InfoRow
            label={t("exam.detail.evaluation")}
            value={t(`exam.eval.${exam.evaluationMethod}`)}
          />
          <InfoRow
            label={t("exam.detail.selection")}
            value={t(`exam.selection.${exam.selectionMethod}`)}
          />
          <InfoRow
            label={t("exam.detail.timeAllowed")}
            value={
              exam.givenTime
                ? `${exam.givenTime.days}d ${exam.givenTime.hours}h ${exam.givenTime.minutes}m`
                : "—"
            }
          />
          <InfoRow
            label={t("exam.detail.passMark")}
            value={String(exam.cuttingPoint)}
          />
          <InfoRow
            label={t("exam.detail.totalPoints")}
            value={String(totalPoints)}
          />
          <InfoRow
            label={t("exam.detail.questions")}
            value={String((exam.questions ?? []).length)}
          />
        </SimpleGrid>
        {(exam.direction?.en || exam.direction?.am) && (
          <>
            <Divider my="md" />
            <InfoRow
              label={t("exam.detail.directions")}
              value={[exam.direction?.en, exam.direction?.am]
                .filter(Boolean)
                .join(" / ")}
            />
          </>
        )}
      </Paper>

      {/* Questions */}
      <Paper withBorder radius="lg" p="lg">
        <Group justify="space-between" mb="md">
          <Title order={5}>
            {t("exam.detail.questionsSection", { pts: totalPoints })}
          </Title>
          <RequirePermission anyOf={[LICENSE_PERMISSIONS.MANAGE_EXAMS]} hideOnly>
            <Group gap="xs">
              {paperLocked && (
                <Badge size="sm" variant="light" color="gray">
                  {t("exam.paperLocked")}
                </Badge>
              )}
              <Tooltip
                label={t("exam.paperLockedHint", {
                  count: registrations?.length ?? 0,
                })}
                disabled={!paperLocked}
                multiline
                w={280}
              >
                {/* Wrapped: a disabled Mantine Button fires no pointer events,
                    so the tooltip needs an enabled element to hang off. */}
                <Box>
                  <Button
                    variant="light"
                    size="xs"
                    leftSection={<IconPlus size={14} />}
                    onClick={openAssignModal}
                    disabled={paperLocked}
                  >
                    {t("exam.manageQuestions")}
                  </Button>
                </Box>
              </Tooltip>
            </Group>
          </RequirePermission>
        </Group>
        {(exam.questions ?? []).length === 0 ? (
          <Alert
            color={paperLocked ? "red" : "gray"}
            icon={<IconInfoCircle size={16} />}
          >
            {paperLocked
              ? t("exam.paperLockedHint", { count: registrations?.length ?? 0 })
              : t("exam.noQuestionsAssigned")}
          </Alert>
        ) : (
          <Stack gap="md">
            {(exam.questions ?? []).map((q, i) => (
              <Paper key={q.id} withBorder p="md" radius="md">
                <Group justify="space-between" mb="xs">
                  <Text fz="sm" fw={700}>
                    {t("exam.detail.questionLabel")} {i + 1}
                  </Text>
                  <Group gap={4}>
                    <Badge
                      size="xs"
                      variant="light"
                      color={q.form === "ESSAY" ? "blue" : "violet"}
                    >
                      {t(`exam.formType.${q.form}`)}
                    </Badge>
                    <Badge size="xs" variant="light" color="gray">
                      {q.points} pts
                    </Badge>
                  </Group>
                </Group>
                <Text fz="sm">{q.title[locale]}</Text>
              </Paper>
            ))}
          </Stack>
        )}
      </Paper>

      {/* Exam-day operations: who sat the paper, and what went wrong */}
      <ExamCandidatesPanel examId={exam.id} />
      <ExamIncidentsPanel examId={exam.id} />

      <RecordResultModal exam={exam} opened={recordOpened} onClose={closeRecord} />

      {/* Question assignment modal */}
      <Modal
        opened={assignOpened}
        onClose={closeAssign}
        title={`${t("exam.manageQuestions")} — ${exam.title[locale]}`}
        size="xl"
        radius="lg"
      >
        <Stack gap="md">
          {exam.selectionMethod === "MANUAL" ? (
            <>
              <QuestionAssigner
                available={eligibleQuestions}
                assigned={draftQuestions}
                onChange={setDraftQuestions}
                mode="manual"
              />
              <ModalFooter>
                <Button variant="default" onClick={closeAssign} size="sm">
                  {t("exam.cancel")}
                </Button>
                <RequirePermission anyOf={[LICENSE_PERMISSIONS.MANAGE_EXAMS]} hideOnly>
                  <Button onClick={handleAssign} size="sm" loading={isAssigning}>
                    {t("exam.saveAssignments")}
                  </Button>
                </RequirePermission>
              </ModalFooter>
            </>
          ) : (
            <>
              <Text fz="sm" c="dimmed">{t('exam.randomHintServer')}</Text>
              <Group gap="sm">
                <NumberInput
                  placeholder={t("exam.assigner.selectCount")}
                  value={randomCount}
                  onChange={(v) => setRandomCount(Number(v))}
                  min={1}
                  size="xs"
                  style={{ width: 80 }}
                />
                <RequirePermission anyOf={[LICENSE_PERMISSIONS.MANAGE_EXAMS]} hideOnly>
                  <Button size="xs" variant="light" loading={isDrawing} onClick={handleRandomSelect}>
                    {t('exam.randomSelect')}
                  </Button>
                </RequirePermission>
              </Group>
              <QuestionAssigner
                available={eligibleQuestions}
                assigned={draftQuestions}
                onChange={setDraftQuestions}
                mode="random"
              />
              <ModalFooter>
                <Button variant="default" onClick={closeAssign} size="sm">
                  {t("exam.cancel")}
                </Button>
                <RequirePermission anyOf={[LICENSE_PERMISSIONS.MANAGE_EXAMS]} hideOnly>
                  <Button onClick={handleAssign} size="sm" loading={isAssigning}>
                    {t("exam.saveAssignments")}
                  </Button>
                </RequirePermission>
              </ModalFooter>
            </>
          )}
        </Stack>
      </Modal>
    </Stack>
  );
}
