import {
  Badge,
  Box,
  Button,
  Code,
  Group,
  Loader,
  Modal,
  Pagination,
  Paper,
  ScrollArea,
  Stack,
  Text,
  Timeline,
} from "@mantine/core";
import { useDisclosure } from "@mantine/hooks";
import { IconHistory } from "@tabler/icons-react";
import moment from "moment";
import React, { useCallback, useEffect, useState } from "react";
import type { AdminActivityLog } from "@/lib/features/activity-logs/query";
import { getEDMLogs } from "@/lib/features/edm/query";

// ── diff helpers ─────────────────────────────────────────────────────────────

type DiffType = "added" | "removed" | "changed";
interface DiffEntry { path: string; type: DiffType; before: any; after: any }

function flattenObject(obj: any, prefix = ""): Record<string, any> {
  if (obj === null || obj === undefined || typeof obj !== "object") {
    return prefix ? { [prefix]: obj } : {};
  }
  if (Array.isArray(obj)) return { [prefix]: JSON.stringify(obj) };
  return Object.entries(obj).reduce<Record<string, any>>((acc, [k, v]) => {
    const key = prefix ? `${prefix}.${k}` : k;
    if (v !== null && typeof v === "object" && !Array.isArray(v)) {
      Object.assign(acc, flattenObject(v, key));
    } else {
      acc[key] = Array.isArray(v) ? JSON.stringify(v) : v;
    }
    return acc;
  }, {});
}

function diffObjects(before: any, after: any): DiffEntry[] {
  const flatBefore = flattenObject(before ?? {});
  const flatAfter = flattenObject(after ?? {});
  const allKeys = new Set([...Object.keys(flatBefore), ...Object.keys(flatAfter)]);
  const entries: DiffEntry[] = [];
  for (const key of Array.from(allKeys).sort()) {
    const bVal = flatBefore[key];
    const aVal = flatAfter[key];
    if (!(key in flatBefore)) entries.push({ path: key, type: "added", before: undefined, after: aVal });
    else if (!(key in flatAfter)) entries.push({ path: key, type: "removed", before: bVal, after: undefined });
    else if (String(bVal) !== String(aVal)) entries.push({ path: key, type: "changed", before: bVal, after: aVal });
  }
  return entries;
}

function parseJson(val: any) {
  try { return typeof val === "string" ? JSON.parse(val) : val; } catch { return null; }
}

function truncate(val: any): string {
  const s = String(val ?? "");
  if (s.length > 120) return `${s.slice(0, 120)}… (${s.length} chars)`;
  return s;
}

// ── Diff modal ────────────────────────────────────────────────────────────────

function DiffModal({ log, opened, onClose }: { log: AdminActivityLog | null; opened: boolean; onClose: () => void }) {
  if (!log) return null;
  const before = parseJson(log.before_payload);
  const after = parseJson(log.payload);
  const changes = log.method === "DELETE" ? [] : diffObjects(before ?? {}, after ?? {});

  return (
    <Modal opened={opened} onClose={onClose} title="Change Details" size="lg" centered>
      <Stack gap="sm">
        <Group>
          <Badge color={log.method === "DELETE" ? "red" : log.method === "POST" ? "green" : "blue"} variant="light">
            {log.method}
          </Badge>
          <Text size="sm" c="dimmed">{moment(log.performed_at).format("DD MMM YYYY, HH:mm")}</Text>
          <Text size="sm" c="dimmed">by {log.admin_email}</Text>
        </Group>

        {log.method === "DELETE" && before && (
          <Stack gap="xs">
            <Text size="sm" fw={600}>Deleted template snapshot</Text>
            <Code block fz="xs" style={{ whiteSpace: "pre-wrap" }}>
              {JSON.stringify({ id: before.id, name: before.name, updated_at: before.updated_at }, null, 2)}
            </Code>
          </Stack>
        )}

        {log.method === "POST" && after && (
          <Stack gap="xs">
            <Text size="sm" fw={600}>Created with fields</Text>
            {Object.entries(after).map(([k, v]) => (
              <Group key={k} gap="xs" wrap="nowrap" align="flex-start">
                <Text size="sm" fw={500} style={{ minWidth: 120 }}>{k}:</Text>
                <Text size="sm" c="dimmed" style={{ flex: 1 }}>{truncate(v)}</Text>
              </Group>
            ))}
          </Stack>
        )}

        {changes.length > 0 && (
          <Stack gap="xs">
            <Text size="sm" fw={600}>Field changes</Text>
            {changes.map((d) => (
              <Paper key={d.path} withBorder radius="sm" p="xs"
                style={{ borderLeft: `3px solid var(--mantine-color-${d.type === "added" ? "green" : d.type === "removed" ? "red" : "blue"}-5)` }}
              >
                <Text size="xs" fw={600} ff="monospace" mb={4}>{d.path}</Text>
                {d.type !== "added" && (
                  <Group gap="xs" wrap="nowrap">
                    <Badge size="xs" color="red" variant="light">before</Badge>
                    <Text size="xs" c="dimmed" style={{ flex: 1 }}>{truncate(d.before)}</Text>
                  </Group>
                )}
                {d.type !== "removed" && (
                  <Group gap="xs" wrap="nowrap" mt={2}>
                    <Badge size="xs" color="green" variant="light">after</Badge>
                    <Text size="xs" style={{ flex: 1 }}>{truncate(d.after)}</Text>
                  </Group>
                )}
              </Paper>
            ))}
          </Stack>
        )}

        {changes.length === 0 && log.method !== "DELETE" && log.method !== "POST" && (
          <Text size="sm" c="dimmed">No field-level diff available.</Text>
        )}
      </Stack>
    </Modal>
  );
}

// ── Main panel ────────────────────────────────────────────────────────────────

interface Props {
  templateId?: string | null;
}

const PAGE_SIZE = 15;

export function EDMLogsPanel({ templateId }: Props) {
  const [logs, setLogs] = useState<AdminActivityLog[]>([]);
  const [loading, setLoading] = useState(false);
  const [page, setPage] = useState(1);
  const [total, setTotal] = useState(0);
  const [selected, setSelected] = useState<AdminActivityLog | null>(null);
  const [diffOpened, { open: openDiff, close: closeDiff }] = useDisclosure(false);

  const fetchLogs = useCallback(async (p: number) => {
    setLoading(true);
    try {
      const res = await getEDMLogs(templateId ?? null, p, PAGE_SIZE);
      if (res.success && res.data) {
        setLogs(res.data.items ?? []);
        setTotal(res.data.totalCount ?? 0);
      }
    } catch {
      // silent
    } finally {
      setLoading(false);
    }
  }, [templateId]);

  useEffect(() => { fetchLogs(page); }, [page, fetchLogs]);

  const totalPages = Math.ceil(total / PAGE_SIZE);

  const methodColor = (m: string) => {
    if (m === "POST") return "green";
    if (m === "DELETE") return "red";
    return "blue";
  };

  const actionLabel = (m: string) => {
    if (m === "POST") return "Created";
    if (m === "DELETE") return "Deleted";
    if (m === "PATCH" || m === "PUT") return "Updated";
    return m;
  };

  return (
    <Stack gap="md">
      <Group justify="space-between">
        <Text fw={700} size="lg">Change history</Text>
        <Button size="sm" variant="subtle" onClick={() => fetchLogs(page)} loading={loading}>
          Refresh
        </Button>
      </Group>

      {loading && logs.length === 0 && (
        <Group justify="center" py="xl"><Loader size="sm" /></Group>
      )}

      {!loading && logs.length === 0 && (
        <Text size="md" c="dimmed" ta="center" py="xl">No changes recorded yet.</Text>
      )}

      {logs.length > 0 && (
        <ScrollArea>
          <Timeline active={-1} bulletSize={22} lineWidth={2}>
            {logs.map((log) => {
              const action = actionLabel(log.method);
              const before = parseJson(log.before_payload);
              const after = parseJson(log.payload);
              const changes = log.method !== "DELETE" ? diffObjects(before ?? {}, after ?? {}) : [];
              return (
                <Timeline.Item
                  key={log.id}
                  bullet={
                    <Box
                      style={{
                        width: 22, height: 22, borderRadius: "50%",
                        background: `var(--mantine-color-${methodColor(log.method)}-1)`,
                        display: "flex", alignItems: "center", justifyContent: "center",
                      }}
                    >
                      <IconHistory size={12} color={`var(--mantine-color-${methodColor(log.method)}-6)`} />
                    </Box>
                  }
                  title={
                    <Group gap="xs">
                      <Badge color={methodColor(log.method)} variant="light" size="sm">{action}</Badge>
                      {changes.length > 0 && (
                        <Badge color="gray" variant="outline" size="sm">{changes.length} field{changes.length > 1 ? "s" : ""} changed</Badge>
                      )}
                    </Group>
                  }
                >
                  <Text size="sm" c="dimmed" mt={2}>
                    {moment(log.performed_at).fromNow()} · {log.admin_email}
                  </Text>
                  {(changes.length > 0 || log.method === "DELETE" || log.method === "POST") && (
                    <Button
                      size="compact-xs"
                      variant="subtle"
                      mt={4}
                      onClick={() => { setSelected(log); openDiff(); }}
                    >
                      View details
                    </Button>
                  )}
                </Timeline.Item>
              );
            })}
          </Timeline>
        </ScrollArea>
      )}

      {totalPages > 1 && (
        <Group justify="center">
          <Pagination total={totalPages} value={page} onChange={setPage} size="sm" />
        </Group>
      )}

      <DiffModal log={selected} opened={diffOpened} onClose={closeDiff} />
    </Stack>
  );
}
