"use client";

import * as React from "react";
import {
  CircleCheckIcon,
  ExternalLinkIcon,
  FileTextIcon,
  XCircleIcon,
} from "lucide-react";

import { Button } from "@/components/ui/button";
import {
  Dialog,
  DialogBody,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import { EmptyState } from "@/components/ui/empty-state";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { Typography } from "@/components/ui/typography";
import { useVerifyProviderDocument } from "@/hooks/providers/useVerifyProviderDocument";
import { useTranslation } from "@/hooks/useTranslation";
import { VerificationStatus } from "@/types/provider";
import type { ProviderDocument } from "@/types/provider";

import { DocumentStatusBadge } from "./ProviderStatusBadge";
import { getPreviewKind } from "./lib/document-preview";

/** Backend requires at least 3 characters when rejecting */
const MIN_REASON_LENGTH = 3;

type Step = "preview" | "approve" | "reject";

interface DocumentReviewDialogProps {
  document: ProviderDocument | null;
  providerId: string;
  onClose: () => void;
}

/**
 * KYC document review: preview the file, then approve or reject it.
 *
 * ONE dialog that switches its body between the preview and a confirmation
 * step, rather than stacking a ConfirmDialog on top of this one. Both the
 * Dialog overlay and the AlertDialog overlay sit at `z-50`, so nesting them
 * would rely on portal mount order to decide what paints on top — and the
 * design system already prescribes swapping content in a single dialog over
 * stacking two (see calendar-dialog.tsx).
 *
 * The confirmation step is a real gate: nothing is sent until it is confirmed.
 */
export function DocumentReviewDialog({
  document,
  providerId,
  onClose,
}: DocumentReviewDialogProps) {
  const { t } = useTranslation();
  const [step, setStep] = React.useState<Step>("preview");
  const [reason, setReason] = React.useState("");
  const [reasonError, setReasonError] = React.useState(false);
  const [previewFailed, setPreviewFailed] = React.useState(false);

  const verifyDocument = useVerifyProviderDocument(providerId, {
    onSuccess: onClose,
  });

  // Keep the last document while the dialog animates out so its content doesn't
  // blank mid-transition, and reset the wizard when a DIFFERENT document is
  // opened. Both are done by adjusting state during render (React's documented
  // pattern) rather than a ref read or an effect — the compiler rules reject
  // those, and an effect would also render one frame of stale content first.
  const [shownDocument, setShownDocument] =
    React.useState<ProviderDocument | null>(null);

  if (document && document !== shownDocument) {
    const isDifferentDocument = document.id !== shownDocument?.id;
    setShownDocument(document);
    if (isDifferentDocument) {
      setStep("preview");
      setReason("");
      setReasonError(false);
      setPreviewFailed(false);
    }
  }

  const shown = document ?? shownDocument;
  if (!shown) return null;

  const documentLabel = t(`providers.documentType.${shown.documentType}`);
  const previewKind = getPreviewKind(shown.documentUrl);

  function handleConfirm() {
    if (!shown) return;

    if (step === "reject") {
      const trimmed = reason.trim();
      if (trimmed.length < MIN_REASON_LENGTH) {
        setReasonError(true);
        return;
      }
      verifyDocument.mutate({
        documentId: shown.id,
        status: VerificationStatus.REJECTED,
        rejectionReason: trimmed,
      });
      return;
    }

    verifyDocument.mutate({
      documentId: shown.id,
      status: VerificationStatus.APPROVED,
    });
  }

  return (
    <Dialog
      open={!!document}
      onOpenChange={(next) => {
        if (!next) onClose();
      }}
    >
      <DialogContent className="sm:max-w-2xl">
        <DialogHeader>
          <DialogTitle>
            {step === "preview"
              ? t("providers.review.title")
              : t(`providers.review.${step}Title`)}
          </DialogTitle>
          <DialogDescription>
            {step === "preview"
              ? documentLabel
              : t(`providers.review.${step}Description`, {
                  document: documentLabel,
                })}
          </DialogDescription>
        </DialogHeader>

        {step === "preview" ? (
          <DialogBody className="flex flex-col gap-3">
            <div className="flex flex-wrap items-center gap-2">
              <DocumentStatusBadge status={shown.status} />
              <Button
                variant="link"
                size="sm"
                nativeButton={false}
                render={
                  <a
                    href={shown.documentUrl}
                    target="_blank"
                    rel="noopener noreferrer"
                  />
                }
              >
                <ExternalLinkIcon />
                {t("providers.detail.documents.openOriginal")}
              </Button>
            </div>

            <DocumentPreview
              url={shown.documentUrl}
              kind={previewKind}
              failed={previewFailed}
              onFail={() => setPreviewFailed(true)}
            />

            {shown.status === VerificationStatus.REJECTED &&
              shown.rejectionReason && (
                <Typography as="p" variant="caption" color="secondary">
                  {t("providers.detail.documents.rejectionReason", {
                    reason: shown.rejectionReason,
                  })}
                </Typography>
              )}
          </DialogBody>
        ) : step === "reject" ? (
          <DialogBody className="flex flex-col gap-1.5">
            <Label htmlFor="document-rejection-reason">
              {t("providers.review.rejectReasonLabel")}
            </Label>
            <Textarea
              id="document-rejection-reason"
              value={reason}
              placeholder={t("providers.review.rejectReasonPlaceholder")}
              aria-invalid={reasonError}
              disabled={verifyDocument.isPending}
              onChange={(event) => {
                setReason(event.target.value);
                if (
                  reasonError &&
                  event.target.value.trim().length >= MIN_REASON_LENGTH
                ) {
                  setReasonError(false);
                }
              }}
            />
            {reasonError && (
              <Typography
                as="p"
                variant="caption"
                className="mt-0.5 text-destructive"
              >
                {t("providers.validation.rejectionReasonMin")}
              </Typography>
            )}
          </DialogBody>
        ) : null}

        <DialogFooter>
          {step === "preview" ? (
            <>
              <Button
                variant="destructive"
                disabled={shown.status === VerificationStatus.REJECTED}
                onClick={() => setStep("reject")}
              >
                <XCircleIcon />
                {t("providers.review.reject")}
              </Button>
              <Button
                disabled={shown.status === VerificationStatus.APPROVED}
                onClick={() => setStep("approve")}
              >
                <CircleCheckIcon />
                {t("providers.review.approve")}
              </Button>
            </>
          ) : (
            <>
              <Button
                variant="secondary"
                disabled={verifyDocument.isPending}
                onClick={() => setStep("preview")}
              >
                {t("common.back")}
              </Button>
              <Button
                variant={step === "reject" ? "destructive" : "default"}
                loading={verifyDocument.isPending}
                onClick={handleConfirm}
              >
                {t(`providers.review.${step}`)}
              </Button>
            </>
          )}
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}

function DocumentPreview({
  url,
  kind,
  failed,
  onFail,
}: {
  url: string;
  kind: ReturnType<typeof getPreviewKind>;
  failed: boolean;
  onFail: () => void;
}) {
  const { t } = useTranslation();

  if (failed || kind === "unsupported") {
    return (
      <div className="rounded-[12px] border border-border bg-background">
        {/* The two cases need different explanations: a load failure is about
            this file being unreachable, not about its type being unsupported. */}
        <EmptyState
          size="sm"
          icon={<FileTextIcon />}
          title={t(
            failed
              ? "providers.review.previewFailed"
              : "providers.review.previewUnavailable",
          )}
          description={t(
            failed
              ? "providers.review.previewFailedDescription"
              : "providers.review.previewUnavailableDescription",
          )}
        />
      </div>
    );
  }

  if (kind === "pdf") {
    return (
      <iframe
        src={url}
        title={t("providers.review.title")}
        className="h-[50svh] w-full rounded-[12px] border border-border bg-white"
      />
    );
  }

  return (
    <div className="flex max-h-[50svh] justify-center overflow-auto rounded-[12px] border border-border bg-background p-2">
      {/* Plain <img>: these are arbitrary signed object-storage URLs from the
          backend, which next/image would need an allow-listed remotePattern for. */}
      {/* eslint-disable-next-line @next/next/no-img-element */}
      <img
        src={url}
        alt=""
        className="max-w-full object-contain"
        onError={onFail}
      />
    </div>
  );
}
