import {
  Alert,
  Anchor,
  Badge,
  Button,
  Card,
  Group,
  Modal,
  SimpleGrid,
  Stack,
  Text,
  Textarea,
} from "@mantine/core";
import { useQuery, useMutation } from "@tanstack/react-query";
import {
  AlertTriangle,
  ClipboardCheck,
  Clock,
  FilePlus2,
  FileX2,
} from "lucide-react";
import { useState } from "react";
import { useFileViewer } from "@edr/ui-common";

import { useAuth } from "@/auth/useAuth";
import { FREIGHT_PERMS, hasPermission } from "@/lib/permissions";
import { fetchViewableFile } from "@/services/files.service";
import { api } from "@/services/api";
import type { Company } from "@/types/customer";
import { formatDate, humanize } from "./format";

/** Friendly labels for the proposed-change snapshot keys (UpdateProfileDto). */
export const FIELD_LABELS: Record<string, string> = {
  companyName: "Company name",
  companyEmail: "Company email",
  companyPhone: "Company phone",
  companyLocation: "Location",
  companyAddress: "Address",
  tin: "TIN",
  vatNumber: "VAT number",
  fanNumber: "FAN number",
  nationality: "Nationality",
  licenceNumber: "Licence number",
  contactPersonName: "Contact person",
  contactPersonPosition: "Contact position",
  contactPersonEmail: "Contact email",
  contactPersonPhone: "Contact phone",
  generalManagerName: "General manager",
  generalManagerEmail: "GM email",
  generalManagerPhone: "GM phone",
  poaName: "PoA name",
  poaPhone: "PoA phone",
  poaEmail: "PoA email",
  poaLocation: "PoA location",
  poaAddress: "PoA address",
  region: "Region",
  zone: "Zone",
  woreda: "Woreda",
  kebele: "Kebele",
  houseNo: "House no.",
  statusDescription: "eTrade status",
  dateRegistered: "Date registered",
  renewedFrom: "Renewed from",
  renewalDate: "Renewal date",
  renewedTo: "Renewed to",
  etradePhone: "eTrade phone",
  ownerPassportNumber: "Owner passport number",
};

/** Best-effort current value on the live company for a proposed field key. */
export function currentValue(company: Company, key: string): string {
  const c = company as unknown as Record<string, unknown>;
  const attrs = (company.attributes ?? {}) as Record<string, unknown>;
  const map: Record<string, unknown> = {
    companyName: c.name,
    companyEmail: c.email,
    companyPhone: c.phone,
    companyLocation: c.country,
    companyAddress: c.address,
    tin: c.tin,
    vatNumber: c.vatNumber,
    fanNumber: c.fanNumber,
    nationality: c.nationality,
    contactPersonName: c.contactPersonName ?? attrs.contactPersonName,
    contactPersonPhone: c.contactPersonPhone ?? attrs.contactPersonPhone,
    generalManagerName: c.generalManagerName ?? attrs.generalManagerName,
    generalManagerEmail: c.generalManagerEmail ?? attrs.generalManagerEmail,
    generalManagerPhone: c.generalManagerPhone ?? attrs.generalManagerPhone,
  };
  const v = key in map ? map[key] : (c[key] ?? attrs[key]);
  return v === null || v === undefined || v === "" ? "—" : String(v);
}

/** Subject a staged `snapshot.faydaIdentity` blob belongs to, from which of its `*FaydaSub` keys is present. */
function faydaIdentitySubject(
  snapshot: Record<string, unknown>,
): "owner" | "poa" | null {
  if ("ownerFaydaSub" in snapshot) return "owner";
  if ("poaFaydaSub" in snapshot) return "poa";
  return null;
}

/**
 * `stageIdentityChange` writes a nested `snapshot.faydaIdentity` object
 * (attrs-key names like `ownerEmail`, not top-level DTO keys), so the generic
 * `DiffRow` loop below can't render it — it would just stringify to
 * `[object Object]`. Render it as its own before/after block instead, using
 * the company's current `identity.owner`/`identity.poa` as the "before" side.
 */
function FaydaIdentityDiff({
  company,
  snapshot,
}: {
  company: Company;
  snapshot: Record<string, unknown>;
}) {
  const subject = faydaIdentitySubject(snapshot);
  if (!subject) return null;
  const current =
    subject === "owner" ? company.identity?.owner : company.identity?.poa;
  const read = (key: string) => snapshot[`${subject}${key}`] as string | undefined;
  const verifiedAt = read("FaydaVerifiedAt");
  const fields: { label: string; from?: string | null; to?: string }[] = [
    { label: "Name", from: current?.name, to: read("Name") },
    { label: "Email", from: current?.email, to: read("Email") },
    { label: "Phone", from: current?.phone, to: read("Phone") },
    { label: "Address", from: current?.address, to: read("Address") },
  ].filter((f) => f.to !== undefined);

  return (
    <Stack gap={8}>
      <Group gap={8}>
        <Text size="sm" fw={600} c="edr-text">
          {subject === "owner" ? "Owner re-verification" : "PoA re-verification"}
        </Text>
        {verifiedAt && (
          <Text size="xs" c="dimmed">
            Verified {formatDate(verifiedAt)}
          </Text>
        )}
      </Group>
      {fields.length > 0 ? (
        <SimpleGrid cols={{ base: 1, sm: 2, lg: 3 }} spacing="lg">
          {fields.map((f) => (
            <DiffRow
              key={f.label}
              label={f.label}
              from={f.from?.trim() ? f.from : "—"}
              to={f.to?.trim() ? f.to : "—"}
            />
          ))}
        </SimpleGrid>
      ) : (
        <Text size="sm" c="dimmed">
          Identity re-verified — no name/email/phone/address change.
        </Text>
      )}
    </Stack>
  );
}

export function DiffRow({
  label,
  from,
  to,
}: {
  label: string;
  from: string;
  to: string;
}) {
  const changed = from !== to;
  return (
    <Stack gap={2}>
      <Text size="xs" fw={600} c="edr-muted" tt="uppercase">
        {label}
      </Text>
      <Group gap={8} wrap="nowrap" align="center">
        <Text
          size="sm"
          c="dimmed"
          td={changed ? "line-through" : undefined}
          style={{ wordBreak: "break-word" }}
        >
          {from}
        </Text>
        {changed && (
          <>
            <Text size="sm" c="edr-muted">
              →
            </Text>
            <Text size="sm" fw={600} c="edr-text">
              {to}
            </Text>
          </>
        )}
      </Group>
    </Stack>
  );
}

/**
 * Backoffice review surface for a customer's staged profile edits. Shows the
 * pending change request as a proposed-vs-current diff with Approve / Reject /
 * Request changes actions. Past decisions live in the History tab's unified
 * timeline (see {@link CompanyTimeline}), not here.
 */
export function ChangeRequestReview({ company }: { company: Company }) {
  const { user } = useAuth();
  const canReview = hasPermission(user, FREIGHT_PERMS.customers.verify);
  const query = useQuery(
    api.customers.changeRequests.queryOptions({ input: { id: company.id } }),
  );
  const approve = useMutation(
    api.customers.approveChangeRequest.mutationOptions(),
  );
  const reject = useMutation(
    api.customers.rejectChangeRequest.mutationOptions(),
  );
  const requestChanges = useMutation(
    api.customers.requestChangeRequestChanges.mutationOptions(),
  );

  const { view, viewer } = useFileViewer();
  const [actionTarget, setActionTarget] = useState<{
    id: string;
    kind: "reject" | "request-changes";
  } | null>(null);
  const [note, setNote] = useState("");

  const requests = query.data ?? [];
  const pending = requests.find((r) => r.status === "pending");

  if (!pending) return null;

  const proposedKeys = pending
    ? Object.keys(pending.snapshot ?? {}).filter((k) => k !== "faydaIdentity")
    : ([] as string[]);
  const faydaIdentitySnapshot = pending?.snapshot?.faydaIdentity as
    | Record<string, unknown>
    | undefined;
  const docCount = pending?.documentFileIds?.length ?? 0;
  const licenseChanges = pending?.licenseChanges ?? [];
  const documentChanges = pending?.documentChanges ?? [];

  const confirmAction = () => {
    if (!actionTarget) return;
    const mutation = actionTarget.kind === "reject" ? reject : requestChanges;
    mutation.mutate(
      { id: actionTarget.id, note: note.trim() },
      {
        onSuccess: () => {
          setActionTarget(null);
          setNote("");
        },
      },
    );
  };

  return (
    <>
      {pending && (
        <Card withBorder>
          <Stack gap="md">
            <Group justify="space-between">
              <Group gap="sm">
                <ClipboardCheck size={18} className="text-edr-muted" />
                <Text fw={600} c="edr-text">
                  Profile changes awaiting review
                </Text>
                <Badge color="yellow" variant="light" radius="md">
                  Pending
                </Badge>
              </Group>
              <Text size="xs" c="dimmed">
                Submitted {formatDate(pending.submittedAt ?? pending.createdAt)}
              </Text>
            </Group>

            {pending.note && (
              <Alert
                color="yellow"
                variant="light"
                icon={<AlertTriangle size={16} />}
              >
                Changes were requested on an earlier round of this same
                submission: <strong>{pending.note}</strong> — check whether
                this resubmission actually addresses it before approving.
              </Alert>
            )}

            {proposedKeys.length > 0 ? (
              <SimpleGrid cols={{ base: 1, sm: 2, lg: 3 }} spacing="lg">
                {proposedKeys.map((key) => (
                  <DiffRow
                    key={key}
                    label={FIELD_LABELS[key] ?? humanize(key)}
                    from={currentValue(company, key)}
                    to={
                      pending.snapshot[key] === null ||
                      pending.snapshot[key] === undefined ||
                      pending.snapshot[key] === ""
                        ? "—"
                        : String(pending.snapshot[key])
                    }
                  />
                ))}
              </SimpleGrid>
            ) : !faydaIdentitySnapshot ? (
              <Text size="sm" c="dimmed">
                No field changes — document uploads only.
              </Text>
            ) : null}

            {faydaIdentitySnapshot && (
              <FaydaIdentityDiff company={company} snapshot={faydaIdentitySnapshot} />
            )}

            {documentChanges.length > 0 && (
              <Stack gap={8}>
                <Text size="sm" fw={600} c="edr-text">
                  Document changes
                </Text>
                {documentChanges.map((c, i) => (
                  <Group key={`${c.fileId}-${i}`} gap={8} wrap="nowrap">
                    {c.op === "add" ? (
                      <FilePlus2 size={15} className="text-edr-muted" />
                    ) : (
                      <FileX2 size={15} className="text-edr-muted" />
                    )}
                    <Badge
                      size="sm"
                      radius="sm"
                      variant="light"
                      color={c.op === "add" ? "green" : "red"}
                    >
                      {c.op === "add" ? "Add" : "Remove"}
                    </Badge>
                    <Anchor
                      component="button"
                      type="button"
                      size="sm"
                      onClick={() =>
                        void fetchViewableFile(
                          c.fileId,
                          c.fileName ?? humanize(c.code),
                        ).then(view)
                      }
                      style={{
                        textDecoration:
                          c.op === "remove" ? "line-through" : undefined,
                      }}
                    >
                      {c.fileName ?? humanize(c.code)}
                    </Anchor>
                    <Text size="xs" c="dimmed">
                      {humanize(c.code)}
                    </Text>
                  </Group>
                ))}
              </Stack>
            )}

            {docCount > 0 && (
              <Stack gap={8}>
                <Text size="sm" fw={600} c="edr-text">
                  Documents uploaded with this request
                </Text>
                {pending!.documentFileIds.map((fileId, i) => (
                  <Group key={fileId} gap={8} wrap="nowrap">
                    <FilePlus2 size={15} className="text-edr-muted" />
                    <Anchor
                      component="button"
                      type="button"
                      size="sm"
                      onClick={() =>
                        void fetchViewableFile(fileId, `Document ${i + 1}`).then(
                          view,
                        )
                      }
                    >
                      Document {i + 1}
                    </Anchor>
                  </Group>
                ))}
              </Stack>
            )}

            {licenseChanges.length > 0 && (
              <Stack gap={8}>
                <Text size="sm" fw={600} c="edr-text">
                  Business license changes
                </Text>
                {licenseChanges.map((c, i) => (
                  <Group key={`${c.fileId}-${i}`} gap={8} wrap="nowrap">
                    {c.op === "add" ? (
                      <FilePlus2 size={15} className="text-edr-muted" />
                    ) : (
                      <FileX2 size={15} className="text-edr-muted" />
                    )}
                    <Badge
                      size="sm"
                      radius="sm"
                      variant="light"
                      color={c.op === "add" ? "green" : "red"}
                    >
                      {c.op === "add" ? "Add" : "Remove"}
                    </Badge>
                    <Anchor
                      component="button"
                      type="button"
                      size="sm"
                      onClick={() =>
                        void fetchViewableFile(
                          c.fileId,
                          c.fileName ?? "License document",
                        ).then(view)
                      }
                      style={{
                        textDecoration:
                          c.op === "remove" ? "line-through" : undefined,
                      }}
                    >
                      {c.fileName ?? "License document"}
                    </Anchor>
                  </Group>
                ))}
              </Stack>
            )}

            {/* Reviewing the diff is `customers:view`; deciding on it is
                `customers:verify`. Without it the request stays readable but
                un-actionable. */}
            {canReview && (
              <Group justify="flex-end" gap="sm">
                <Button
                  variant="light"
                  color="red"
                  onClick={() => {
                    setActionTarget({ id: pending.id, kind: "reject" });
                    setNote("");
                  }}
                >
                  Reject
                </Button>
                <Button
                  variant="light"
                  color="yellow"
                  onClick={() => {
                    setActionTarget({ id: pending.id, kind: "request-changes" });
                    setNote("");
                  }}
                >
                  Request changes
                </Button>
                <Button
                  color="edr-green"
                  loading={approve.isPending}
                  onClick={() => approve.mutate({ id: pending.id })}
                >
                  Approve changes
                </Button>
              </Group>
            )}
          </Stack>
        </Card>
      )}

      <Modal
        opened={actionTarget !== null}
        onClose={() => setActionTarget(null)}
        title={
          actionTarget?.kind === "reject" ? "Reject changes" : "Request changes"
        }
        centered
        radius="lg"
      >
        <Stack gap="md">
          <Alert
            color={actionTarget?.kind === "reject" ? "red" : "yellow"}
            variant="light"
            icon={<AlertTriangle size={18} />}
          >
            {actionTarget?.kind === "reject"
              ? "The customer will see this note and can amend and resubmit."
              : "The customer will see this note and can keep editing this same request — no need to start over."}
          </Alert>
          <Textarea
            label={
              actionTarget?.kind === "reject"
                ? "Reason for rejection"
                : "What needs to change"
            }
            placeholder="e.g. The company address doesn't match the trade license."
            autosize
            minRows={3}
            value={note}
            onChange={(e) => setNote(e.currentTarget.value)}
            required
          />
          <Group justify="flex-end" gap="sm">
            <Button
              variant="default"
              onClick={() => setActionTarget(null)}
              disabled={reject.isPending || requestChanges.isPending}
            >
              Cancel
            </Button>
            <Button
              color={actionTarget?.kind === "reject" ? "red" : "yellow"}
              loading={reject.isPending || requestChanges.isPending}
              disabled={note.trim().length === 0}
              onClick={confirmAction}
            >
              {actionTarget?.kind === "reject"
                ? "Reject changes"
                : "Request changes"}
            </Button>
          </Group>
        </Stack>
      </Modal>

      {viewer}
    </>
  );
}

/** Compact "N changes pending" pill for the customer list/detail header. */
export function ChangeRequestPendingBadge({ companyId }: { companyId: string }) {
  const query = useQuery(
    api.customers.changeRequests.queryOptions({ input: { id: companyId } }),
  );
  const pending = (query.data ?? []).some((r) => r.status === "pending");
  if (!pending) return null;
  return (
    <Badge
      color="yellow"
      variant="light"
      radius="md"
      leftSection={<Clock size={12} />}
    >
      Changes pending review
    </Badge>
  );
}
