import { useEffect, useState } from "react";
import { Alert, Button, Group, List, Modal, Stack, Text } from "@mantine/core";
import { AlertTriangle } from "lucide-react";

/**
 * Global API error modal.
 *
 * The axios client emits every failed request (with a server response) through
 * `emitApiError`; this modal — mounted once at the app root — shows the
 * SERVER'S actual `message` instead of a generic "Request failed with status
 * code NNN". Pages listed in EXCLUDED_PATH_PATTERNS (warehouse, first/last
 * mile, onboarding/auth screens) keep their own inline error handling and
 * never trigger it.
 */

export interface ApiErrorPayload {
  /** Server messages — the `message` field (string or class-validator array). */
  messages: string[];
  statusCode?: number;
  /** API path that failed, shown as small helper text. */
  path?: string;
}

type Listener = (payload: ApiErrorPayload) => void;

let listener: Listener | null = null;

/** Current-page path patterns where the global modal must stay silent. */
const EXCLUDED_PATH_PATTERNS = [
  /^\/auth/,
  /^\/callback/,
  /warehouse/i,
  /first-mile/i,
  /last-mile/i,
  /onboard/i,
  /register/i,
];

export function isGlobalErrorModalSuppressed(pathname: string): boolean {
  return EXCLUDED_PATH_PATTERNS.some((re) => re.test(pathname));
}

export function emitApiError(payload: ApiErrorPayload): void {
  if (isGlobalErrorModalSuppressed(window.location.pathname)) return;
  listener?.(payload);
}

/** Pull the server `message` out of an axios-style error. */
export function extractApiErrorPayload(error: unknown): ApiErrorPayload | null {
  const err = error as {
    response?: {
      status?: number;
      data?: {
        message?: string | string[];
        error?: string;
        statusCode?: number;
        path?: string;
      };
    };
  };
  const response = err?.response;
  if (!response) return null; // network error / cancellation — not ours
  const data = response.data;
  const raw = data?.message;
  const messages = Array.isArray(raw)
    ? raw.filter((m): m is string => typeof m === "string" && m.length > 0)
    : typeof raw === "string" && raw.length > 0
      ? [raw]
      : [];
  if (messages.length === 0) {
    messages.push(
      data?.error ?? `Request failed with status code ${response.status}`,
    );
  }
  return {
    messages,
    statusCode: data?.statusCode ?? response.status,
    path: data?.path,
  };
}

export function ApiErrorModal() {
  const [payload, setPayload] = useState<ApiErrorPayload | null>(null);

  useEffect(() => {
    listener = (next) => {
      // Don't stack identical messages while the modal is already showing them.
      setPayload((current) =>
        current && current.messages.join("\n") === next.messages.join("\n")
          ? current
          : next,
      );
    };
    return () => {
      listener = null;
    };
  }, []);

  const close = () => setPayload(null);

  return (
    <Modal
      opened={payload !== null}
      onClose={close}
      centered
      radius="md"
      // Mounted at the app root, so its portal is FIRST in <body> — at the
      // default z-index (200) any page modal opened later (create schedule,
      // allocation wizard, …) paints over it and the error hides underneath.
      // Hoist above every Mantine overlay and the react-hot-toast layer (9999).
      zIndex={10000}
      title={
        <Group gap="xs">
          <AlertTriangle size={18} color="var(--mantine-color-red-6)" />
          <Text fw={700}>Request failed</Text>
        </Group>
      }
    >
      {payload && (
        <Stack gap="md">
          <Alert color="red" variant="light">
            {payload.messages.length === 1 ? (
              <Text size="sm">{payload.messages[0]}</Text>
            ) : (
              <List size="sm" spacing={4}>
                {payload.messages.map((m, i) => (
                  <List.Item key={i}>{m}</List.Item>
                ))}
              </List>
            )}
          </Alert>
          {(payload.statusCode || payload.path) && (
            <Text size="xs" c="dimmed">
              {payload.statusCode ? `Status ${payload.statusCode}` : null}
              {payload.statusCode && payload.path ? " · " : null}
              {payload.path}
            </Text>
          )}
          <Group justify="flex-end">
            <Button variant="default" onClick={close}>
              Close
            </Button>
          </Group>
        </Stack>
      )}
    </Modal>
  );
}
