import { useCallback, useEffect, useRef, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import {
  Alert,
  Box,
  Button,
  Checkbox,
  Group,
  Image,
  Loader,
  Modal,
  Paper,
  PinInput,
  Stack,
  Text,
  TextInput,
} from "@mantine/core";
import {
  ArrowLeft,
  Download,
  FileSignature,
  Printer,
  RotateCw,
  ShieldCheck,
} from "lucide-react";
import toast from "react-hot-toast";

import { ContractSignSuccessModal } from "@/components/contracts/ContractSignSuccessModal";
import { ContractSignaturePad } from "@/components/bookings/ContractSignaturePad";
import { StampUpload } from "@/components/contracts/StampUpload";
import { contractsService } from "@/services/contracts.service";
import { api } from "@/services/api";
import { extractApiError } from "@/utils/result";

const CONSENT_TEXT =
  "I have read the entire contract and agree to its terms.";

/**
 * Customer contract preview + sign. Customers must scroll through the full
 * contract and accept the terms before signing.
 */
export default function ContractViewPage() {
  const { id } = useParams<{ id: string }>();
  const navigate = useNavigate();
  const qc = useQueryClient();
  const iframeRef = useRef<HTMLIFrameElement>(null);

  const [signOpen, setSignOpen] = useState(false);
  const [otpOpen, setOtpOpen] = useState(false);
  const [otpCode, setOtpCode] = useState("");
  const [otpError, setOtpError] = useState<string | null>(null);
  const [successOpen, setSuccessOpen] = useState(false);
  const [signerName, setSignerName] = useState("");
  const [signatureData, setSignatureData] = useState<string | null>(null);
  const [stampData, setStampData] = useState<string | null>(null);
  const [drawNew, setDrawNew] = useState(false);
  const [hasScrolledToBottom, setHasScrolledToBottom] = useState(false);
  const [agreedToTerms, setAgreedToTerms] = useState(false);

  // The signing OTP goes to the signed-in user's own registered phone AND email,
  // resolved server-side from their account (the same contacts the server
  // verifies against). One code covers both, so a delayed SMS doesn't strand the
  // signer. The client never picks the contacts, so send and verify can't
  // disagree; we only get back a masked hint of where it landed.
  const [otpSentTo, setOtpSentTo] = useState<string | null>(null);

  const { data, isLoading, isError, refetch } = useQuery({
    queryKey: ["contract-view", id],
    queryFn: () => contractsService.getContractView(id!),
    enabled: Boolean(id),
  });

  const savedSignatureImage = data?.savedSignature?.signatureImageUrl ?? null;
  const usingSaved = Boolean(savedSignatureImage) && !drawNew;
  const canProceedToSign = hasScrolledToBottom && agreedToTerms;

  const checkScrollBottom = useCallback(() => {
    try {
      const win = iframeRef.current?.contentWindow;
      if (!win?.document?.documentElement) return;
      const el = win.document.documentElement;
      const threshold = 48;
      if (el.scrollHeight <= el.clientHeight + threshold) {
        setHasScrolledToBottom(true);
        return;
      }
      if (el.scrollTop + el.clientHeight >= el.scrollHeight - threshold) {
        setHasScrolledToBottom(true);
      }
    } catch {
      /* srcDoc is same-origin; ignore edge cases */
    }
  }, []);

  const handleIframeLoad = () => {
    checkScrollBottom();
    try {
      const win = iframeRef.current?.contentWindow;
      win?.addEventListener("scroll", checkScrollBottom);
    } catch {
      /* ignore */
    }
  };

  useEffect(() => {
    return () => {
      try {
        iframeRef.current?.contentWindow?.removeEventListener(
          "scroll",
          checkScrollBottom,
        );
      } catch {
        /* ignore */
      }
    };
  }, [checkScrollBottom]);

  // Send (or resend) the fresh OTP challenge to the contract company's phone. On
  // success we swap the signature modal for the OTP entry modal and remember the
  // masked destination the server reported.
  const sendOtpMutation = useMutation({
    mutationFn: () => contractsService.sendSigningOtp(id!),
    onSuccess: (res) => {
      setOtpSentTo(res.sentTo);
      setSignOpen(false);
      setOtpError(null);
      setOtpOpen(true);
    },
    onError: (err) =>
      toast.error(
        extractApiError(err).message ?? "Failed to send verification code",
      ),
  });

  const signMutation = useMutation({
    mutationFn: () =>
      contractsService.signContract(id!, {
        role: "CUSTOMER",
        signatureImageBase64: usingSaved
          ? (savedSignatureImage as string)
          : (signatureData as string),
        stampImageBase64: stampData as string,
        signerDisplayName: signerName.trim(),
        consentText: CONSENT_TEXT,
        otp: otpCode.trim(),
      }),
    onSuccess: () => {
      setOtpOpen(false);
      setOtpCode("");
      setSuccessOpen(true);
      void refetch();
      void qc.invalidateQueries({
        queryKey: api.contracts.get.queryKey({ id: id! }),
      });
    },
    onError: (err) =>
      setOtpError(
        extractApiError(err).message ?? "Failed to verify code and sign",
      ),
  });

  const openSign = () => {
    if (!canProceedToSign) return;
    setSignerName(data?.savedSignature?.signerDisplayName ?? "");
    setSignatureData(null);
    setStampData(null);
    setDrawNew(false);
    setSignOpen(true);
  };

  const confirmSign = () => {
    if (!signerName.trim()) return;
    const image = usingSaved ? savedSignatureImage : signatureData;
    if (!image || !stampData) return;
    // The server resolves and validates the signer's own contacts; if the
    // account has neither phone nor email it returns a clear 400 that surfaces
    // via the mutation's onError.
    setOtpCode("");
    sendOtpMutation.mutate();
  };

  const confirmOtp = () => {
    if (otpCode.trim().length !== 6) return;
    setOtpError(null);
    signMutation.mutate();
  };

  const handlePrint = () => iframeRef.current?.contentWindow?.print();

  const downloadPdf = useCallback(async () => {
    if (!id) return;
    try {
      const blob = await contractsService.downloadContractDocument(id);
      const url = URL.createObjectURL(blob);
      const a = document.createElement("a");
      a.href = url;
      a.download = `contract-${data?.reference ?? id}.pdf`;
      a.click();
      URL.revokeObjectURL(url);
    } catch {
      toast.error("Could not download contract PDF.");
    }
  }, [id, data?.reference]);

  if (isLoading) {
    return (
      <Group justify="center" mih="40vh" align="center">
        <Loader color="edr-green" />
      </Group>
    );
  }

  if (isError || !data) {
    return (
      <Box p="xl">
        <Text c="dimmed">Could not load contract.</Text>
        <Button variant="default" mt="md" onClick={() => navigate(-1)}>
          Go back
        </Button>
      </Box>
    );
  }

  return (
    <Box p={{ base: "md", md: "xl" }} pb={data.canSignCustomer ? 120 : undefined}>
      <Box maw={920} mx="auto">
        <Group justify="space-between" wrap="wrap" gap="sm" mb="md">
          <Button
            variant="subtle"
            color="gray"
            leftSection={<ArrowLeft size={16} />}
            onClick={() => navigate(`/contracts/${id}`)}
          >
            Back to contract
          </Button>
          <Group gap="sm">
            <Button
              variant="default"
              leftSection={<Printer size={16} />}
              onClick={handlePrint}
            >
              Print
            </Button>
            <Button
              variant="default"
              leftSection={<Download size={16} />}
              onClick={() => void downloadPdf()}
            >
              Download PDF
            </Button>
          </Group>
        </Group>

        {/* Signed before company stamps were required — re-signing is the only
            way to attach one, and EDR cannot counter-sign until it is there. */}
        {data.canSignCustomer && data.status === "SIGNED_CUSTOMER" && (
          <Alert color="orange" variant="light" radius="md" mb="md">
            This contract was signed before a company stamp was required. Please
            sign again and attach your stamp so EDR can counter-sign it.
          </Alert>
        )}

        {data.canSignCustomer && !hasScrolledToBottom && (
          <Alert color="blue" variant="light" radius="md" mb="md">
            Please scroll through the entire contract before signing.
          </Alert>
        )}

        <Paper withBorder radius="lg" p={0} style={{ overflow: "hidden" }}>
          <iframe
            ref={iframeRef}
            srcDoc={data.html}
            title="Contract document"
            onLoad={handleIframeLoad}
            style={{
              width: "100%",
              minHeight: "80vh",
              border: "none",
              background: "white",
            }}
          />
        </Paper>
      </Box>

      {data.canSignCustomer && hasScrolledToBottom && (
        <Paper
          withBorder
          radius="lg"
          p="md"
          style={{
            position: "fixed",
            bottom: 0,
            left: 0,
            right: 0,
            zIndex: 100,
            borderTop: "1px solid var(--mantine-color-gray-3)",
            background: "var(--mantine-color-body)",
          }}
        >
          <Box maw={920} mx="auto">
            <Stack gap="sm">
              <Checkbox
                checked={agreedToTerms}
                onChange={(e) => setAgreedToTerms(e.currentTarget.checked)}
                disabled={!hasScrolledToBottom}
                label={CONSENT_TEXT}
                description={
                  hasScrolledToBottom
                    ? "You may now sign the contract."
                    : "Read the full contract above before you can agree and sign."
                }
              />
              <Group justify="flex-end">
                <Button
                  color="edr-green"
                  leftSection={<FileSignature size={16} />}
                  disabled={!canProceedToSign}
                  onClick={openSign}
                >
                  {usingSaved ? "Approve & sign" : "Sign contract"}
                </Button>
              </Group>
            </Stack>
          </Box>
        </Paper>
      )}

      <Modal
        opened={signOpen}
        onClose={() => setSignOpen(false)}
        title={usingSaved ? "Approve signature" : "Sign contract"}
        centered
        radius="lg"
      >
        <Stack gap="md">
          <Text size="sm" c="dimmed">
            {data.reference} — your signature is stored securely on the
            contract.
          </Text>
          <TextInput
            label="Full name"
            value={signerName}
            onChange={(e) => setSignerName(e.currentTarget.value)}
          />
          {usingSaved ? (
            <Stack gap="xs">
              <Paper
                withBorder
                radius="md"
                p="xs"
                style={{ borderStyle: "dashed" }}
              >
                <Image
                  src={savedSignatureImage ?? undefined}
                  alt="Saved signature"
                  fit="contain"
                  h={140}
                />
              </Paper>
              <Button
                variant="subtle"
                size="compact-xs"
                color="edr-green"
                onClick={() => {
                  setDrawNew(true);
                  setSignatureData(null);
                }}
              >
                Draw a new signature instead
              </Button>
            </Stack>
          ) : (
            <ContractSignaturePad onChange={setSignatureData} />
          )}

          <StampUpload
            value={stampData}
            onChange={setStampData}
            description="Attach your official company stamp or seal — it is applied to the contract next to your signature."
          />

          <Group justify="flex-end" gap="sm">
            <Button variant="default" onClick={() => setSignOpen(false)}>
              Cancel
            </Button>
            <Button
              color="edr-green"
              loading={sendOtpMutation.isPending}
              disabled={
                sendOtpMutation.isPending ||
                !signerName.trim() ||
                (!usingSaved && !signatureData) ||
                !stampData
              }
              onClick={confirmSign}
            >
              Continue to verification
            </Button>
          </Group>
        </Stack>
      </Modal>

      <Modal
        opened={otpOpen}
        onClose={() => setOtpOpen(false)}
        title="Verify it's you"
        centered
        radius="lg"
      >
        <Stack gap="md">
          <Group gap="sm" wrap="nowrap">
            <Box
              w={40}
              h={40}
              style={{
                display: "flex",
                alignItems: "center",
                justifyContent: "center",
                borderRadius: 12,
                background: "var(--mantine-color-edr-green-0)",
                flexShrink: 0,
              }}
            >
              <ShieldCheck
                size={20}
                color="var(--mantine-color-edr-green-6)"
              />
            </Box>
            <Text size="sm" c="dimmed">
              For security, enter the 6-digit code we sent to your registered
              contacts
              {otpSentTo ? (
                <>
                  {" "}
                  <Text span fw={600} c="edr-text">
                    {otpSentTo}
                  </Text>
                </>
              ) : null}{" "}
              to confirm and apply your signature.
            </Text>
          </Group>

          {otpError && (
            <Alert color="red" variant="light" radius="md">
              {otpError}
            </Alert>
          )}

          <Stack gap={6}>
            <Text size="sm" fw={500} c="edr-text">
              Verification code
            </Text>
            <PinInput
              length={6}
              type="number"
              oneTimeCode
              value={otpCode}
              placeholder="0"
              disabled={signMutation.isPending}
              styles={{ input: { textAlign: "center" } }}
              onChange={setOtpCode}
            />
          </Stack>

          <Group justify="space-between" gap="sm">
            <Button
              variant="subtle"
              color="gray"
              size="compact-sm"
              leftSection={<RotateCw size={14} />}
              loading={sendOtpMutation.isPending}
              disabled={sendOtpMutation.isPending || signMutation.isPending}
              onClick={() => {
                setOtpError(null);
                sendOtpMutation.mutate();
              }}
            >
              Resend code
            </Button>
            <Group gap="sm">
              <Button
                variant="default"
                onClick={() => setOtpOpen(false)}
                disabled={signMutation.isPending}
              >
                Cancel
              </Button>
              <Button
                color="edr-green"
                leftSection={<FileSignature size={16} />}
                loading={signMutation.isPending}
                disabled={signMutation.isPending || otpCode.trim().length !== 6}
                onClick={confirmOtp}
              >
                Verify &amp; sign
              </Button>
            </Group>
          </Group>
        </Stack>
      </Modal>

      <ContractSignSuccessModal
        opened={successOpen}
        reference={data.reference}
        message="Your signature has been recorded. EDR staff will counter-sign to complete the contract."
        onClose={() => {
          setSuccessOpen(false);
          navigate(`/contracts/${id}`);
        }}
      />
    </Box>
  );
}
