import { useState } from "react";
import {
  ActionIcon,
  Alert,
  Badge,
  Button,
  Checkbox,
  Drawer,
  Group,
  Paper,
  Progress,
  Stack,
  Text,
  TextInput,
  Tooltip,
} from "@mantine/core";
import {
  IconAlertCircle,
  IconCheck,
  IconDownload,
  IconEye,
  IconFileText,
  IconRotate,
  IconX,
} from "@tabler/icons-react";
import { useTranslation } from "react-i18next";
import {
  conditionHolds,
  useClearDocumentReviewMutation,
  useGetDocumentReviewsQuery,
  useLocalized,
  useReviewDocumentMutation,
  type Attachment,
  type DocumentRequirement,
} from "@ema-platform/api";
import { notifications } from "@mantine/notifications";

interface DocumentsTabProps {
  applicationId: string;
  attachments: Attachment[];
  /** From the licence type config, so completeness is measured against rules. */
  requirements: DocumentRequirement[];
  /** Applicant answers used to evaluate conditional document requirements. */
  formData: Record<string, Record<string, unknown>>;
  /** documentKey -> remark. Owned by the review page. */
  flags: Record<string, string>;
  onToggleFlag: (documentKey: string) => void;
  onFlagRemark: (documentKey: string, remark: string) => void;
}

/**
 * The reviewer's document workspace.
 *
 * Previously a list with View and Download buttons that had no handlers at
 * all — the officer could see that a document existed but not what was in it,
 * which makes "approve documents" an act of faith. This previews inline,
 * measures what is uploaded against what the licence type requires, and lets
 * each document be flagged with its own reason.
 */
export function DocumentsTab({
  applicationId,
  attachments,
  requirements,
  formData,
  flags,
  onToggleFlag,
  onFlagRemark,
}: DocumentsTabProps) {
  const { t } = useTranslation();
  const localized = useLocalized();
  const [preview, setPreview] = useState<Attachment | null>(null);
  const [rejecting, setRejecting] = useState<Record<string, string>>({});

  // Verdicts are persisted per document, so an accept survives a reload and
  // is visible to whoever picks the application up next.
  const { data: reviews = [] } = useGetDocumentReviewsQuery(applicationId, {
    skip: !applicationId,
  });
  const [reviewDocument, { isLoading: saving }] = useReviewDocumentMutation();
  const [clearReview] = useClearDocumentReviewMutation();

  const verdictFor = (documentKey: string) =>
    reviews.find((review) => review.documentKey === documentKey);

  async function decide(
    documentKey: string,
    decision: "ACCEPTED" | "REJECTED",
    attachmentId?: string,
  ) {
    const reason = rejecting[documentKey]?.trim();
    if (decision === "REJECTED" && !reason) {
      // The applicant is shown this verbatim, so refuse to send an empty one.
      notifications.show({
        color: "red",
        title: t("review.documents.reasonRequired", "A reason is required"),
        message: "",
      });
      return;
    }
    try {
      await reviewDocument({
        id: applicationId,
        documentKey,
        decision,
        reason: decision === "REJECTED" ? reason : undefined,
        attachmentId,
      }).unwrap();
      setRejecting((prev) => {
        const next = { ...prev };
        delete next[documentKey];
        return next;
      });
    } catch {
      notifications.show({
        color: "red",
        title: t("review.documents.saveFailed", "Could not save the verdict"),
        message: "",
      });
    }
  }

  const uploadedKeys = new Set(attachments.map((a) => a.documentKey));
  const requirementByKey = new Map(requirements.map((r) => [r.key, r]));
  const mandatory = requirements.filter(
    (r) =>
      r.mode === "ALWAYS" ||
      (r.mode === "CONDITIONAL" &&
        conditionHolds(r.conditionExpression, formData)),
  );
  const missing = mandatory.filter((r) => !uploadedKeys.has(r.key));
  const completeness = mandatory.length
    ? Math.round(((mandatory.length - missing.length) / mandatory.length) * 100)
    : 100;

  const previewFile = preview?.files?.[0];
  const isImage = previewFile?.mimeType?.startsWith("image/");
  const isPdf = previewFile?.mimeType === "application/pdf";

  return (
    <Stack gap="md">
      {/* Completeness against the licence type's own requirement list. */}
      <Paper withBorder p="md">
        <Group justify="space-between" mb="xs">
          <Text fw={600} size="sm">
            {t("review.documents.completeness", "Required documents")}
          </Text>
          <Text size="sm" c={missing.length ? "orange" : "teal"} fw={600}>
            {mandatory.length - missing.length}/{mandatory.length}
          </Text>
        </Group>
        <Progress
          value={completeness}
          color={missing.length ? "orange" : "teal"}
          aria-label={t("review.documents.completenessLabel", {
            value: completeness,
            defaultValue: "{{value}}% of required documents uploaded",
          })}
        />
        {missing.length > 0 && (
          <Alert
            mt="sm"
            color="orange"
            icon={<IconAlertCircle size={16} />}
            variant="light"
          >
            <Text size="sm">
              {t("review.documents.missing", "Not yet uploaded")}:{" "}
              {missing.map((r) => localized(r.name) || r.key).join(", ")}
            </Text>
          </Alert>
        )}
      </Paper>

      {attachments.map((attachment) => {
        const file = attachment.files?.[0];
        const flagged = attachment.documentKey in flags;
        const verdict = verdictFor(attachment.documentKey);
        const pendingReject = attachment.documentKey in rejecting;
        return (
          <Paper withBorder p="md" key={attachment.id}>
            <Group justify="space-between" wrap="nowrap" align="flex-start">
              <Group gap="sm" wrap="nowrap" style={{ minWidth: 0, flex: 1 }}>
                <IconFileText size={20} stroke={1.6} />
                <div style={{ minWidth: 0, flex: 1 }}>
                  {/* Badges sit beside the name, not in the outer nowrap row —
                      that row also has to fit six action buttons, so a long
                      name plus badges there overflowed its box and the
                      opaque badge painted over the bleeding text. Nested here
                      with its own wrap, the name truncates cleanly instead. */}
                  <Group gap={6} wrap="wrap" align="center">
                    <Text
                      size="sm"
                      fw={500}
                      truncate
                      style={{ maxWidth: "100%" }}
                    >
                      {localized(
                        requirementByKey.get(attachment.documentKey)?.name,
                      ) || attachment.documentKey}
                    </Text>
                    {verdict && (
                      <Tooltip
                        label={
                          verdict.reason ??
                          t("review.documents.reviewedBy", {
                            name: verdict.reviewedByName ?? "—",
                            defaultValue: "Reviewed by {{name}}",
                          })
                        }
                      >
                        <Badge
                          color={
                            verdict.decision === "ACCEPTED" ? "teal" : "red"
                          }
                          variant="light"
                          size="sm"
                          leftSection={
                            verdict.decision === "ACCEPTED" ? (
                              <IconCheck size={11} />
                            ) : (
                              <IconX size={11} />
                            )
                          }
                        >
                          {verdict.decision === "ACCEPTED"
                            ? t("review.documents.accepted", "Accepted")
                            : t("review.documents.rejected", "Rejected")}
                        </Badge>
                      </Tooltip>
                    )}
                    {flagged && (
                      <Badge color="orange" variant="light" size="sm">
                        {t("review.documents.flagged", "Correction requested")}
                      </Badge>
                    )}
                  </Group>
                  <Text size="xs" c="dimmed" truncate>
                    {file?.originalName ??
                      t("review.documents.noFile", "No file")}
                  </Text>
                </div>
              </Group>

              <Group gap="xs" wrap="nowrap">
                <Tooltip
                  label={
                    file?.url
                      ? t("review.documents.preview", "Preview")
                      : t(
                          "review.documents.noFileUploaded",
                          "Nothing uploaded yet",
                        )
                  }
                >
                  <span>
                    <Button
                      size="compact-sm"
                      variant="light"
                      leftSection={<IconEye size={14} />}
                      disabled={!file?.url}
                      onClick={() => setPreview(attachment)}
                    >
                      {t("review.documents.view", "View")}
                    </Button>
                  </span>
                </Tooltip>
                <Tooltip
                  label={
                    file?.url
                      ? t("review.documents.download", "Download")
                      : t(
                          "review.documents.noFileUploaded",
                          "Nothing uploaded yet",
                        )
                  }
                >
                  <span>
                    <ActionIcon
                      variant="subtle"
                      disabled={!file?.url}
                      component="a"
                      href={file?.url}
                      download={file?.originalName}
                      target="_blank"
                      rel="noreferrer"
                      aria-label={t("review.documents.download", "Download")}
                    >
                      <IconDownload size={16} />
                    </ActionIcon>
                  </span>
                </Tooltip>

                {/* Accept / Reject are the officer's own record of having
                    checked the file, persisted independently of any
                    adjustment round. */}
                <Tooltip
                  label={
                    file?.url
                      ? t("review.documents.accept", "Accept")
                      : t(
                          "review.documents.nothingToJudge",
                          "Nothing uploaded to judge",
                        )
                  }
                >
                  <span>
                    <ActionIcon
                      variant={
                        verdict?.decision === "ACCEPTED" ? "filled" : "light"
                      }
                      color="teal"
                      loading={saving}
                      disabled={!file?.url}
                      aria-label={t("review.documents.accept", "Accept")}
                      onClick={() =>
                        decide(
                          attachment.documentKey,
                          "ACCEPTED",
                          attachment.id,
                        )
                      }
                    >
                      <IconCheck size={16} />
                    </ActionIcon>
                  </span>
                </Tooltip>
                <Tooltip
                  label={
                    file?.url
                      ? t("review.documents.reject", "Reject")
                      : t(
                          "review.documents.nothingToJudge",
                          "Nothing uploaded to judge",
                        )
                  }
                >
                  <span>
                    <ActionIcon
                      variant={
                        verdict?.decision === "REJECTED" ? "filled" : "light"
                      }
                      color="red"
                      disabled={!file?.url}
                      aria-label={t("review.documents.reject", "Reject")}
                      onClick={() =>
                        setRejecting((prev) => ({
                          ...prev,
                          [attachment.documentKey]: verdict?.reason ?? "",
                        }))
                      }
                    >
                      <IconX size={16} />
                    </ActionIcon>
                  </span>
                </Tooltip>
                {verdict && (
                  <Tooltip label={t("review.documents.clear", "Clear verdict")}>
                    <ActionIcon
                      variant="subtle"
                      color="gray"
                      aria-label={t("review.documents.clear", "Clear verdict")}
                      onClick={() =>
                        clearReview({
                          id: applicationId,
                          documentKey: attachment.documentKey,
                        })
                      }
                    >
                      <IconRotate size={16} />
                    </ActionIcon>
                  </Tooltip>
                )}
                <Checkbox
                  size="xs"
                  checked={flagged}
                  onChange={() => onToggleFlag(attachment.documentKey)}
                  label={t("review.documents.includeInAdjustment", "Send back")}
                />
              </Group>
            </Group>

            {pendingReject && (
              <Group mt="sm" gap="xs" align="flex-start" wrap="nowrap">
                <TextInput
                  style={{ flex: 1 }}
                  size="xs"
                  autoFocus
                  placeholder={t(
                    "review.documents.rejectReason",
                    "Why must this document be corrected?",
                  )}
                  value={rejecting[attachment.documentKey]}
                  onChange={(e) => {
                    // Read before the updater: React nulls `currentTarget`
                    // once the handler returns, and the updater runs later,
                    // during the re-render.
                    const reason = e.currentTarget.value;
                    setRejecting((prev) => ({
                      ...prev,
                      [attachment.documentKey]: reason,
                    }));
                  }}
                />
                <Button
                  size="compact-sm"
                  color="red"
                  loading={saving}
                  disabled={!rejecting[attachment.documentKey]?.trim()}
                  onClick={() =>
                    decide(attachment.documentKey, "REJECTED", attachment.id)
                  }
                >
                  {t("review.documents.confirmReject", "Reject")}
                </Button>
              </Group>
            )}

            {flagged && (
              <TextInput
                mt="sm"
                size="xs"
                placeholder={t(
                  "review.documents.adjustmentNote",
                  "What must the applicant correct?",
                )}
                value={flags[attachment.documentKey]}
                onChange={(e) =>
                  onFlagRemark(attachment.documentKey, e.currentTarget.value)
                }
                error={
                  flags[attachment.documentKey].trim()
                    ? undefined
                    : t(
                        "review.documents.reasonRequired",
                        "A reason is required",
                      )
                }
              />
            )}
          </Paper>
        );
      })}

      <Drawer
        opened={Boolean(preview)}
        onClose={() => setPreview(null)}
        position="right"
        size="xl"
        title={
          preview
            ? localized(requirementByKey.get(preview.documentKey)?.name) ||
              preview.documentKey
            : ""
        }
        // Focus is trapped and returned so keyboard users are not dropped at
        // the top of the page when the drawer closes.
        trapFocus
        returnFocus
      >
        {previewFile?.url ? (
          isPdf ? (
            <iframe
              src={previewFile.url}
              title={
                preview?.documentKey ??
                t("review.documents.previewFallback", "document")
              }
              style={{ width: "100%", height: "80vh", border: "none" }}
            />
          ) : isImage ? (
            <img
              src={previewFile.url}
              alt={
                preview?.documentKey ??
                t("review.documents.previewFallback", "document")
              }
              style={{ maxWidth: "100%" }}
            />
          ) : (
            // Anything the browser will not render inline still gets a way out.
            <Stack align="center" gap="sm" py="xl">
              <Text size="sm" c="dimmed">
                {t(
                  "review.documents.noInlinePreview",
                  "This file type cannot be previewed in the browser.",
                )}
              </Text>
              <Button
                component="a"
                href={previewFile.url}
                target="_blank"
                rel="noreferrer"
                leftSection={<IconDownload size={16} />}
              >
                {t("review.documents.downloadShort", "Download")}
              </Button>
            </Stack>
          )
        ) : null}
      </Drawer>
    </Stack>
  );
}
