import { useMemo, useState } from "react";
import {
  ActionIcon,
  Badge,
  Box,
  Button,
  Group,
  Loader,
  Menu,
  Modal,
  Paper,
  Stack,
  Switch,
  Text,
  TextInput,
  ThemeIcon,
  Tooltip,
} from "@mantine/core";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import {
  Download,
  Eye,
  EyeOff,
  FileText,
  MoreVertical,
  Pencil,
  Share2,
  Trash2,
  Upload,
  UserCheck,
} from "lucide-react";
import toast from "react-hot-toast";
import type { Freight } from "@edr/types";
import { isViewable } from "@edr/ui-common";

import { PhasedFileDropzone } from "@/components/contracts/PhasedFileDropzone";
import { useFileViewer } from "@/hooks/useFileViewer";
import { downloadBookingFile, fetchViewableFile } from "@/services/files.service";
import { glExchangeService } from "@/services/glExchange.service";

const SIDES: Record<Freight.GlExchangeDocument["side"], { label: string; color: string }> =
  {
    ET: { label: "GL Ethiopia", color: "edr-green" },
    DJ: { label: "GL Djibouti", color: "blue" },
  };

function formatBytes(bytes: number): string {
  if (!bytes) return "0 B";
  const units = ["B", "KB", "MB", "GB"];
  const i = Math.floor(Math.log(bytes) / Math.log(1024));
  return `${parseFloat((bytes / 1024 ** i).toFixed(1))} ${units[i]}`;
}

export interface GlExchangePanelProps {
  /** Booking or contract id both desks are working on — the thread key. */
  entityId: string;
}

/**
 * GL Ethiopia ↔ GL Djibouti document exchange. Either desk attaches any file
 * under a title of its own choosing; both desks see the whole thread, only the
 * uploader can change or remove what they posted, and each document is shared
 * with the customer's portal or kept between the desks.
 */
export function GlExchangePanel({ entityId }: GlExchangePanelProps) {
  const queryClient = useQueryClient();
  const { view, viewer } = useFileViewer();
  const [formDoc, setFormDoc] = useState<
    Freight.GlExchangeDocument | "new" | null
  >(null);
  const [pendingDelete, setPendingDelete] =
    useState<Freight.GlExchangeDocument | null>(null);

  const {
    data: documents = [],
    isLoading,
    isError,
  } = useQuery({
    queryKey: ["gl-exchange", entityId],
    queryFn: () => glExchangeService.list(entityId),
    enabled: Boolean(entityId),
  });

  const invalidate = () =>
    queryClient.invalidateQueries({ queryKey: ["gl-exchange", entityId] });

  const removeMutation = useMutation({
    mutationFn: (id: string) => glExchangeService.remove(id),
    onSuccess: async () => {
      setPendingDelete(null);
      await invalidate();
      toast.success("Document removed");
    },
    onError: (e: unknown) =>
      toast.error(e instanceof Error ? e.message : "Could not remove document"),
  });

  const stats = useMemo(
    () => ({
      et: documents.filter((d) => d.side === "ET").length,
      dj: documents.filter((d) => d.side === "DJ").length,
      shared: documents.filter((d) => d.visibleToCustomer).length,
    }),
    [documents],
  );

  return (
    <Stack gap="md">
      <Paper withBorder radius="md" p="md">
        <Group justify="space-between" align="flex-start" wrap="wrap" gap="sm">
          <Group gap={12} wrap="nowrap" style={{ minWidth: 0 }}>
            <ThemeIcon variant="light" color="edr-green" radius="md" size={44}>
              <Share2 size={20} />
            </ThemeIcon>
            <Box style={{ minWidth: 0 }}>
              <Text fw={700} fz={16}>
                Document exchange
              </Text>
              <Text size="xs" c="dimmed">
                Share any document with the other Global Logistics desk. Both
                desks see everything here; only the uploader can edit or remove
                a document, and only documents marked visible reach the customer.
              </Text>
            </Box>
          </Group>
          <Button
            color="edr-green"
            radius="md"
            leftSection={<Upload size={16} />}
            onClick={() => setFormDoc("new")}
          >
            Share document
          </Button>
        </Group>

        {documents.length > 0 ? (
          <Group gap={8} mt="md">
            <Badge variant="light" color="edr-green" radius="sm" tt="none">
              {stats.et} from GL Ethiopia
            </Badge>
            <Badge variant="light" color="blue" radius="sm" tt="none">
              {stats.dj} from GL Djibouti
            </Badge>
            <Badge variant="light" color="gray" radius="sm" tt="none">
              {stats.shared} visible to customer
            </Badge>
          </Group>
        ) : null}
      </Paper>

      {isLoading ? (
        <Group justify="center" py={40} gap={10}>
          <Loader size="sm" color="edr-green" />
          <Text size="sm" c="dimmed">
            Loading shared documents…
          </Text>
        </Group>
      ) : isError ? (
        <Text size="sm" c="red">
          Could not load the shared documents.
        </Text>
      ) : documents.length === 0 ? (
        <EmptyState onShare={() => setFormDoc("new")} />
      ) : (
        <Stack gap={8}>
          {documents.map((doc) => (
            <DocumentRow
              key={doc.id}
              doc={doc}
              onView={view}
              onEdit={() => setFormDoc(doc)}
              onDelete={() => setPendingDelete(doc)}
            />
          ))}
        </Stack>
      )}

      <DocumentFormModal
        entityId={entityId}
        doc={formDoc === "new" ? null : formDoc}
        opened={formDoc != null}
        onClose={() => setFormDoc(null)}
        onSaved={() => {
          setFormDoc(null);
          void invalidate();
        }}
      />

      <Modal
        opened={pendingDelete != null}
        onClose={() => setPendingDelete(null)}
        title={<Text fw={700}>Remove shared document</Text>}
        radius="md"
        size="sm"
      >
        <Stack gap="md">
          <Text size="sm">
            Remove <b>{pendingDelete?.title}</b> from the exchange? The other
            desk — and the customer, if it was shared — will no longer see it.
          </Text>
          <Group justify="flex-end" gap="sm">
            <Button variant="default" onClick={() => setPendingDelete(null)}>
              Cancel
            </Button>
            <Button
              color="red"
              loading={removeMutation.isPending}
              leftSection={<Trash2 size={15} />}
              onClick={() => removeMutation.mutate(pendingDelete!.id)}
            >
              Remove
            </Button>
          </Group>
        </Stack>
      </Modal>

      {viewer}
    </Stack>
  );
}

function EmptyState({ onShare }: { onShare: () => void }) {
  return (
    <Box
      py={44}
      style={{
        borderRadius: 12,
        border: "1px dashed var(--mantine-color-gray-4)",
        background: "var(--mantine-color-gray-0)",
        textAlign: "center",
      }}
    >
      <Stack gap={10} align="center">
        <ThemeIcon variant="light" color="gray" radius="xl" size={48}>
          <FileText size={22} />
        </ThemeIcon>
        <Text size="sm" c="dimmed" maw={380}>
          Nothing shared yet. Anything either desk uploads here — scans,
          correspondence, corrected forms — is visible to the other side
          immediately.
        </Text>
        <Button
          variant="light"
          color="edr-green"
          radius="md"
          leftSection={<Upload size={15} />}
          onClick={onShare}
        >
          Share the first document
        </Button>
      </Stack>
    </Box>
  );
}

function DocumentRow({
  doc,
  onView,
  onEdit,
  onDelete,
}: {
  doc: Freight.GlExchangeDocument;
  onView: (file: { name: string; url: string }) => void;
  onEdit: () => void;
  onDelete: () => void;
}) {
  const side = SIDES[doc.side];
  const canPreview = isViewable({ name: doc.file.name, url: "" });

  return (
    <Paper withBorder radius="md" p="sm">
      <Group justify="space-between" wrap="nowrap" align="flex-start" gap="sm">
        <Group gap={12} wrap="nowrap" align="flex-start" style={{ minWidth: 0 }}>
          <ThemeIcon variant="light" color={side.color} radius="md" size={40}>
            <FileText size={18} />
          </ThemeIcon>
          <Box style={{ minWidth: 0 }}>
            <Group gap={8} wrap="nowrap">
              <Text size="sm" fw={700} truncate>
                {doc.title}
              </Text>
              <Badge size="xs" variant="light" color={side.color} radius="sm" tt="none">
                {side.label}
              </Badge>
              <Badge
                size="xs"
                variant="light"
                color={doc.visibleToCustomer ? "teal" : "gray"}
                radius="sm"
                tt="none"
                leftSection={
                  doc.visibleToCustomer ? <Eye size={11} /> : <EyeOff size={11} />
                }
              >
                {doc.visibleToCustomer ? "Visible to customer" : "GL only"}
              </Badge>
            </Group>
            <Text size="xs" c="dimmed" mt={4} truncate>
              {doc.file.name} · {formatBytes(doc.file.size)} ·{" "}
              {doc.uploadedByName ?? "Global Logistics"} ·{" "}
              {new Date(doc.uploadedAt).toLocaleString("en-GB", {
                day: "numeric",
                month: "short",
                year: "numeric",
                hour: "2-digit",
                minute: "2-digit",
                hour12: false,
              })}
            </Text>
          </Box>
        </Group>

        <Group gap={6} wrap="nowrap">
          {canPreview ? (
            <Tooltip label="Preview">
              <Button
                size="compact-xs"
                variant="default"
                radius="md"
                leftSection={<Eye size={13} />}
                onClick={() =>
                  void fetchViewableFile(doc.file.id, doc.file.name).then(onView)
                }
              >
                View
              </Button>
            </Tooltip>
          ) : null}
          <Tooltip label="Download">
            <Button
              size="compact-xs"
              variant="light"
              radius="md"
              leftSection={<Download size={13} />}
              onClick={() =>
                void downloadBookingFile(doc.file.id, doc.file.name)
              }
            >
              Download
            </Button>
          </Tooltip>
          {doc.canEdit ? (
            <Menu position="bottom-end" radius="md" withinPortal>
              <Menu.Target>
                <ActionIcon variant="subtle" color="gray" aria-label="Document actions">
                  <MoreVertical size={16} />
                </ActionIcon>
              </Menu.Target>
              <Menu.Dropdown>
                <Menu.Item leftSection={<Pencil size={14} />} onClick={onEdit}>
                  Edit title, visibility or file
                </Menu.Item>
                <Menu.Item
                  color="red"
                  leftSection={<Trash2 size={14} />}
                  onClick={onDelete}
                >
                  Remove
                </Menu.Item>
              </Menu.Dropdown>
            </Menu>
          ) : (
            <Tooltip label={`Only ${doc.uploadedByName ?? "the uploader"} can edit this`}>
              <ThemeIcon variant="subtle" color="gray" size={28}>
                <UserCheck size={15} />
              </ThemeIcon>
            </Tooltip>
          )}
        </Group>
      </Group>
    </Paper>
  );
}

function DocumentFormModal({
  entityId,
  doc,
  opened,
  onClose,
  onSaved,
}: {
  entityId: string;
  doc: Freight.GlExchangeDocument | null;
  opened: boolean;
  onClose: () => void;
  onSaved: () => void;
}) {
  const editing = doc != null;
  const [title, setTitle] = useState("");
  const [visible, setVisible] = useState(false);
  const [file, setFile] = useState<File | null>(null);
  // Re-seed the form whenever a different document (or "new") opens it.
  const [seededFor, setSeededFor] = useState<string | null>(null);
  const seedKey = opened ? (doc?.id ?? "new") : null;
  if (seedKey !== seededFor) {
    setSeededFor(seedKey);
    setTitle(doc?.title ?? "");
    setVisible(doc?.visibleToCustomer ?? false);
    setFile(null);
  }

  const save = useMutation({
    mutationFn: () =>
      editing
        ? glExchangeService.update(doc.id, {
            title: title.trim(),
            visibleToCustomer: visible,
            file,
          })
        : glExchangeService.upload(entityId, {
            title: title.trim(),
            visibleToCustomer: visible,
            file: file!,
          }),
    onSuccess: () => {
      toast.success(editing ? "Document updated" : "Document shared");
      onSaved();
    },
    onError: (e: unknown) =>
      toast.error(e instanceof Error ? e.message : "Could not save document"),
  });

  return (
    <Modal
      opened={opened}
      onClose={onClose}
      title={
        <Group gap={8}>
          <Share2 size={18} />
          <Text fw={700}>{editing ? "Edit shared document" : "Share a document"}</Text>
        </Group>
      }
      radius="md"
      size="md"
    >
      <Stack gap="md">
        <TextInput
          label="Document title"
          placeholder="e.g. Corrected packing list for container TCLU1234567"
          description="What the other desk (and the customer, if shared) will see."
          value={title}
          onChange={(e) => setTitle(e.currentTarget.value)}
          maxLength={300}
          required
        />

        <PhasedFileDropzone
          label={editing ? "Replacement file (optional)" : "File"}
          description={
            editing
              ? "Leave empty to keep the current file."
              : "Any document type — PDF, image, spreadsheet."
          }
          accept="*/*"
          value={file}
          onChange={setFile}
          replaceMode={editing}
        />

        <Switch
          checked={visible}
          onChange={(e) => setVisible(e.currentTarget.checked)}
          color="edr-green"
          label="Visible to the customer"
          description="Shows in the customer's booking documents. Off keeps it between the two GL desks."
        />

        <Group justify="flex-end" gap="sm">
          <Button variant="default" onClick={onClose} disabled={save.isPending}>
            Cancel
          </Button>
          <Button
            color="edr-green"
            loading={save.isPending}
            disabled={!title.trim() || (!editing && !file)}
            leftSection={<Upload size={16} />}
            onClick={() => save.mutate()}
          >
            {editing ? "Save changes" : "Share document"}
          </Button>
        </Group>
      </Stack>
    </Modal>
  );
}
