import { useQuery } from "@tanstack/react-query";
import { History, User } from "lucide-react";
import {
  Avatar,
  Badge,
  Box,
  Group,
  Loader,
  Stack,
  Text,
  Timeline,
  Tooltip,
} from "@mantine/core";
import type { Freight } from "@edr/types";

import { contractsService } from "@/services/contracts.service";
import { SectionCard } from "@/components/bookings/detail/SectionCard";
import { ArticleBodyDiff } from "@/components/contracts/ArticleBodyDiff";

interface ContractRevisionTimelineProps {
  contractId: string;
  /** Rendered as a plain block instead of a SectionCard (own-tab layout). */
  bare?: boolean;
}

type Change = Freight.IContractDocumentChange;

/** Badge colour + verb per change kind, so a revision reads at a glance. */
const CHANGE_STYLES: Record<Change["kind"], { color: string; label: string }> = {
  ARTICLE_ADDED: { color: "green", label: "Added" },
  ARTICLE_REMOVED: { color: "red", label: "Removed" },
  ARTICLE_RENAMED: { color: "violet", label: "Renamed" },
  ARTICLE_BODY_CHANGED: { color: "blue", label: "Edited" },
  ARTICLE_REORDERED: { color: "gray", label: "Reordered" },
  DOCUMENT_TITLE_CHANGED: { color: "grape", label: "Title" },
  WHEREAS_CHANGED: { color: "teal", label: "Recitals" },
  FIELD_CHANGED: { color: "orange", label: "Field" },
};

/** Initials for the actor avatar — "Abenezer Haile" → "AH". */
function initials(name: string): string {
  return name
    .split(/\s+/)
    .filter(Boolean)
    .slice(0, 2)
    .map((part) => part[0]?.toUpperCase() ?? "")
    .join("");
}

/** Role slugs arrive like "-marketing-director-"; render them readably. */
function prettyRole(role: string): string {
  const cleaned = role.replace(/^-+|-+$/g, "").replace(/[-_]+/g, " ").trim();
  if (!cleaned) return role;
  return cleaned.charAt(0).toUpperCase() + cleaned.slice(1).toLowerCase();
}

/** What the change applies to — an article title, or the document itself. */
function changeSubject(change: Change): string {
  switch (change.kind) {
    case "DOCUMENT_TITLE_CHANGED":
      return change.fromTitle
        ? `“${change.fromTitle}” → “${change.title}”`
        : change.title;
    case "WHEREAS_CHANGED": {
      const parts: string[] = [];
      if (change.added) parts.push(`+${change.added}`);
      if (change.removed) parts.push(`−${change.removed}`);
      return parts.join(" ") || "changed";
    }
    case "ARTICLE_RENAMED":
      return `“${change.fromTitle}” → “${change.title}”`;
    case "ARTICLE_REORDERED":
      return `${change.title} (${change.fromOrder} → ${change.toOrder})`;
    case "FIELD_CHANGED":
      return `${change.label}: ${change.from ?? "—"} → ${change.to ?? "—"}`;
    default:
      return change.title;
  }
}

function formatWhen(iso: string): string {
  const date = new Date(iso);
  return date.toLocaleString(undefined, {
    dateStyle: "medium",
    timeStyle: "short",
  });
}

/** "3 hours ago" — the at-a-glance read; the exact stamp sits beside it. */
function formatAgo(iso: string): string {
  const seconds = Math.round((Date.now() - new Date(iso).getTime()) / 1000);
  if (seconds < 60) return "just now";
  const units: Array<[Intl.RelativeTimeFormatUnit, number]> = [
    ["year", 31536000],
    ["month", 2592000],
    ["day", 86400],
    ["hour", 3600],
    ["minute", 60],
  ];
  const rtf = new Intl.RelativeTimeFormat(undefined, { numeric: "auto" });
  for (const [unit, secondsPerUnit] of units) {
    if (seconds >= secondsPerUnit) {
      return rtf.format(-Math.floor(seconds / secondsPerUnit), unit);
    }
  }
  return "just now";
}

/**
 * Audit trail of edits to the contract document. The document stays editable
 * through the approval chain, so this is the record of who changed what.
 */
export function ContractRevisionTimeline({
  contractId,
  bare = false,
}: ContractRevisionTimelineProps) {
  const { data: revisions, isLoading } = useQuery({
    queryKey: ["contracts", contractId, "document-revisions"],
    queryFn: () => contractsService.getContractDocumentRevisions(contractId),
  });

  const body = isLoading ? (
    <Group gap="xs">
      <Loader size="xs" />
      <Text size="sm" c="dimmed">
        Loading history…
      </Text>
    </Group>
  ) : !revisions?.length ? (
    <Stack gap={4} align="center" py="xl">
      <History size={26} color="var(--mantine-color-gray-5)" />
      <Text size="sm" fw={500}>
        No edits recorded yet
      </Text>
      <Text size="xs" c="dimmed" ta="center" maw={420}>
        Every change to this contract — its articles during review and approval,
        or its details while the customer can still edit it — is logged here
        with who made it and when.
      </Text>
    </Stack>
  ) : (
    <Timeline
      active={revisions.length}
      bulletSize={28}
      lineWidth={2}
      color="edr-green"
    >
      {revisions.map((revision) => {
        const who = revision.actorName?.trim();
        const role = revision.actorRole ? prettyRole(revision.actorRole) : null;
        return (
          <Timeline.Item
            key={revision.id}
            bullet={
              <Avatar size={26} radius="xl" color="edr-green" variant="light">
                <Text size="10px" fw={700}>
                  {who ? initials(who) : <User size={13} />}
                </Text>
              </Avatar>
            }
            title={
              <Group gap="xs" wrap="wrap" align="baseline">
                <Text size="sm" fw={600}>
                  {who ?? role ?? "Unknown user"}
                </Text>
                {role && who && (
                  <Badge size="xs" variant="light" color="gray" radius="sm">
                    {role}
                  </Badge>
                )}
                <Tooltip label={formatWhen(revision.createdAt)} withArrow>
                  <Text size="xs" c="dimmed">
                    {formatAgo(revision.createdAt)}
                  </Text>
                </Tooltip>
              </Group>
            }
          >
            <Stack gap={6} mt={6} pb="xs">
              {revision.summary && (
                <Text size="xs" c="dimmed">
                  {revision.summary}
                </Text>
              )}
              {revision.changes.map((change, index) => {
                const style = CHANGE_STYLES[change.kind];
                // A body edit carries both texts (revisions recorded before
                // that change don't) — show the word-level diff inline.
                const bodyDiff =
                  change.kind === "ARTICLE_BODY_CHANGED" &&
                  change.fromBody != null &&
                  change.toBody != null
                    ? { from: change.fromBody, to: change.toBody }
                    : null;
                return (
                  <Group key={index} gap="xs" wrap="nowrap" align="flex-start">
                    <Badge
                      size="xs"
                      variant="light"
                      color={style?.color ?? "gray"}
                      style={{ flexShrink: 0 }}
                    >
                      {style?.label ?? change.kind}
                    </Badge>
                    <Box style={{ minWidth: 0, flex: 1 }}>
                      <Text size="xs" style={{ lineHeight: 1.5 }}>
                        {changeSubject(change)}
                      </Text>
                      {bodyDiff && (
                        <ArticleBodyDiff
                          fromBody={bodyDiff.from}
                          toBody={bodyDiff.to}
                        />
                      )}
                    </Box>
                  </Group>
                );
              })}
            </Stack>
          </Timeline.Item>
        );
      })}
    </Timeline>
  );

  if (bare) return body;
  return (
    <SectionCard icon={History} title="Change history">
      {body}
    </SectionCard>
  );
}
