"use client";

import { useCallback, useEffect, useRef, useState } from "react";
import { Archive, ArrowLeft, Copy, Eye, Monitor, MonitorPlay, RotateCcw, Send, Smartphone, Trash2 } from "lucide-react";
import Button from "@/components/ui/Button";
import ConfirmModal from "@/components/ui/ConfirmModal";
import { useBusinessPanelAccess } from "@/hooks/useBusinessPanelAccess";
import { useRouter } from "@/i18n/navigation";
import { useSearchParams } from "next/navigation";
import MenuBoardEditorSettingsPanel from "@/components/business/menu-boards/MenuBoardEditorSettingsPanel";
import MenuBoardModuleSidebar from "@/components/business/menu-boards/MenuBoardModuleSidebar";
import type { MenuBoardEditorTool } from "@/components/business/menu-boards/MenuBoardModuleSidebar";
import MenuBoardPreviewSurface from "@/components/business/menu-boards/MenuBoardPreviewSurface";
import type { MenuBoard } from "@/components/business/menu-boards/types";
import { boardUpdatePayload, responseBody } from "@/components/business/menu-boards/menuBoardRequests";
import { normalizeMenuBoard } from "@/components/business/menu-boards/menuBoardUtils";
import { useMenuBoardText } from "@/components/business/menu-boards/menuBoardI18n";

type Props = {
  boardId: string;
};

export default function MenuBoardDetailClient({ boardId }: Props) {
  const t = useMenuBoardText();
  const router = useRouter();
  const searchParams = useSearchParams();
  const {
    businessId,
    canArchiveMenuBoards,
    canDeleteMenuBoards,
    canDuplicateMenuBoards,
    canPublishMenuBoards,
    canRestoreMenuBoards,
    canUpdateMenuBoards,
  } = useBusinessPanelAccess();
  const [board, setBoard] = useState<MenuBoard | null>(null);
  const [draft, setDraft] = useState<MenuBoard | null>(null);
  const [activeTool, setActiveTool] = useState<MenuBoardEditorTool>(() => searchParams.get("tool") === "design" ? "design" : "general");
  const [loading, setLoading] = useState(true);
  const [saving, setSaving] = useState(false);
  const [error, setError] = useState("");
  const [archiveOpen, setArchiveOpen] = useState(false);
  const [deleteOpen, setDeleteOpen] = useState(false);
  const previewViewportRef = useRef<HTMLDivElement>(null);
  const [previewViewport, setPreviewViewport] = useState({ width: 0, height: 0 });

  const load = useCallback(async () => {
    if (!businessId) return;
    setLoading(true);
    setError("");
    try {
      const response = await fetch(`/api/businesses/${businessId}/menu-boards/${boardId}`, { cache: "no-store" });
      const json = await responseBody(response);
      if (!response.ok) throw new Error(typeof json.error === "string" ? json.error : "Unable to load menu board.");
      const loaded = normalizeMenuBoard(json as unknown as MenuBoard);
      setBoard(loaded);
      setDraft(structuredClone(loaded));
    } catch (cause) {
      setError(cause instanceof Error ? cause.message : "Unable to load menu board.");
    } finally {
      setLoading(false);
    }
  }, [boardId, businessId]);

  // The business context arrives from the session after hydration.
  // eslint-disable-next-line react-hooks/set-state-in-effect
  useEffect(() => { load(); }, [load]);

  useEffect(() => {
    const viewport = previewViewportRef.current;
    if (!viewport) return;

    const measure = () => {
      const { width, height } = viewport.getBoundingClientRect();
      // Use the complete preview canvas so the selected 16:9 or 9:16 board fits edge to edge.
      const next = { width: Math.max(0, Math.floor(width)), height: Math.max(0, Math.floor(height)) };
      setPreviewViewport((current) => current.width === next.width && current.height === next.height ? current : next);
    };

    measure();
    const observer = new ResizeObserver(measure);
    observer.observe(viewport);
    return () => observer.disconnect();
  }, [loading]);

  const hasChanges = Boolean(board && draft && JSON.stringify(board) !== JSON.stringify(draft));

  function openPublishedDisplay() {
    if (!draft || draft.status !== "PUBLISHED") {
      setError("Publish this board before opening its live display.");
      return;
    }
    window.open(`/${draft.publicId ?? draft.displayToken}`, "_blank", "noopener,noreferrer");
  }

  async function publishBoard() {
    if (!businessId || !draft || !canUpdateMenuBoards || !canPublishMenuBoards) return;
    setSaving(true);
    setError("");
    try {
      const response = await fetch(`/api/businesses/${businessId}/menu-boards/${draft.id}`, {
        method: "PATCH",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ ...boardUpdatePayload(draft, "PUBLISHED"), publish: true }),
      });
      const json = await responseBody(response);
      if (!response.ok) throw new Error(typeof json.error === "string" ? json.error : "Unable to publish board.");
      const saved = json as unknown as MenuBoard;
      setBoard(saved);
      setDraft(structuredClone(saved));
    } catch (cause) {
      setError(cause instanceof Error ? cause.message : "Unable to publish board.");
    } finally {
      setSaving(false);
    }
  }

  async function saveBoard() {
    if (!businessId || !draft || !canUpdateMenuBoards) return;
    setSaving(true);
    setError("");
    try {
      const response = await fetch(`/api/businesses/${businessId}/menu-boards/${draft.id}`, {
        method: "PATCH",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(boardUpdatePayload(draft, draft.status)),
      });
      const json = await responseBody(response);
      if (!response.ok) throw new Error(typeof json.error === "string" ? json.error : "Unable to save board.");
      const saved = json as unknown as MenuBoard;
      setBoard(saved);
      setDraft(structuredClone(saved));
    } catch (cause) {
      setError(cause instanceof Error ? cause.message : "Unable to save board.");
    } finally {
      setSaving(false);
    }
  }

  async function duplicateBoard() {
    if (!businessId || !board || !canDuplicateMenuBoards) return;
    setSaving(true);
    setError("");
    try {
      const response = await fetch(`/api/businesses/${businessId}/menu-boards/${board.id}/duplicate`, { method: "POST" });
      const json = await responseBody(response);
      if (!response.ok) throw new Error(typeof json.error === "string" ? json.error : "Unable to duplicate board.");
      const duplicate = json as unknown as MenuBoard;
      router.push(`/dashboard/business/menu-boards/${duplicate.id}`);
    } catch (cause) {
      setError(cause instanceof Error ? cause.message : "Unable to duplicate board.");
    } finally {
      setSaving(false);
    }
  }

  async function archiveBoard() {
    if (!businessId || !draft || !canUpdateMenuBoards || !canArchiveMenuBoards) return;
    setSaving(true);
    setError("");
    try {
      const response = await fetch(`/api/businesses/${businessId}/menu-boards/${draft.id}`, {
        method: "PATCH",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(boardUpdatePayload(draft, "ARCHIVED")),
      });
      const json = await responseBody(response);
      if (!response.ok) throw new Error(typeof json.error === "string" ? json.error : "Unable to archive board.");
      const saved = normalizeMenuBoard(json as unknown as MenuBoard);
      setBoard(saved);
      setDraft(structuredClone(saved));
    } catch (cause) {
      setError(cause instanceof Error ? cause.message : "Unable to archive board.");
    } finally {
      setSaving(false);
      setArchiveOpen(false);
    }
  }

  async function deleteBoard() {
    if (!businessId || !board || !canDeleteMenuBoards) return;
    setSaving(true);
    setError("");
    try {
      const response = await fetch(`/api/businesses/${businessId}/menu-boards/${board.id}`, { method: "DELETE" });
      const json = await responseBody(response);
      if (!response.ok) throw new Error(typeof json.error === "string" ? json.error : "Unable to move board to Trash.");
      router.push("/dashboard/business/menu-boards?view=trash");
    } catch (cause) {
      setError(cause instanceof Error ? cause.message : "Unable to move board to Trash.");
    } finally {
      setSaving(false);
      setDeleteOpen(false);
    }
  }

  async function restoreBoard() {
    if (!businessId || !board || !canRestoreMenuBoards) return;
    setSaving(true);
    setError("");
    try {
      const response = await fetch(`/api/businesses/${businessId}/menu-boards/${board.id}/restore`, { method: "POST" });
      const json = await responseBody(response);
      if (!response.ok) throw new Error(typeof json.error === "string" ? json.error : "Unable to restore board.");
      await load();
    } catch (cause) {
      setError(cause instanceof Error ? cause.message : "Unable to restore board.");
    } finally {
      setSaving(false);
    }
  }

  if (loading) return <DetailSkeleton />;

  if (!board || !draft) {
    return (
      <div className="space-y-4">
        <Button type="button" variant="secondary" onClick={() => router.push("/dashboard/business/menu-boards")}>
          <ArrowLeft className="h-4 w-4" />
          {t("back")} {t("menuBoards").toLowerCase()}
        </Button>
        <div className="rounded-xl border border-[var(--border)] bg-[var(--bg-elevated)] p-8 text-center">
          <h1 className="text-lg font-semibold text-[var(--text)]">{t("menuBoard")} not found</h1>
          <p className="mt-2 text-sm text-[var(--text-muted)]">{error || "This board may have been removed or you may not have access."}</p>
        </div>
      </div>
    );
  }

  const previewRatio = draft.orientation === "LANDSCAPE" ? 16 / 9 : 9 / 16;
  const previewIsHeightBound = previewViewport.width / Math.max(previewViewport.height, 1) > previewRatio;
  const previewHeight = previewViewport.width && previewViewport.height
    ? previewIsHeightBound ? previewViewport.height : previewViewport.width / previewRatio
    : undefined;
  const previewWidth = previewHeight ? previewHeight * previewRatio : undefined;

  return (
    <div className="fixed inset-0 z-[80] overflow-y-auto bg-[var(--bg)] text-[var(--text)]">
      <WorkspaceTopBar
        draft={draft}
        hasChanges={hasChanges}
        saving={saving}
        canUpdate={canUpdateMenuBoards}
        canPublish={canPublishMenuBoards}
        canDuplicate={canDuplicateMenuBoards}
        canArchive={canArchiveMenuBoards}
        canRestore={canRestoreMenuBoards}
        canDelete={canDeleteMenuBoards}
        onBack={() => router.push("/dashboard/business/menu-boards")}
        onPreview={openPublishedDisplay}
        onSave={saveBoard}
        onPublish={publishBoard}
        onDuplicate={duplicateBoard}
        onArchive={() => setArchiveOpen(true)}
        onRestore={restoreBoard}
        onDelete={() => setDeleteOpen(true)}
        t={t}
      />

      <div className="grid items-start gap-4 p-3 sm:p-5 xl:grid-cols-[180px_minmax(420px,500px)_minmax(0,1fr)] xl:gap-5">
        <MenuBoardModuleSidebar
          activeTool={activeTool}
          onToolChange={setActiveTool}
          onBack={() => router.push("/dashboard/business/menu-boards")}
        />
        <MenuBoardEditorSettingsPanel
          businessId={businessId}
          board={draft}
          activeTool={activeTool}
          disabled={!canUpdateMenuBoards || saving || draft.status === "ARCHIVED"}
          onChange={setDraft}
        />

        <main className="z-10 min-w-0 self-start xl:sticky xl:top-24">
          <section className="space-y-4">
          {error && <p className="mb-4 rounded-lg border border-red-500/30 bg-red-500/10 px-3 py-2 text-sm text-red-400">{error}</p>}
          <div className="flex justify-center">
            <div className="inline-flex rounded-xl border border-[var(--border)] bg-[var(--bg)] p-1">
              <button
                type="button"
                disabled={!canUpdateMenuBoards || saving || draft.status === "ARCHIVED"}
                onClick={() => setDraft({ ...draft, orientation: "LANDSCAPE" })}
                className={`inline-flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-semibold transition ${draft.orientation === "LANDSCAPE" ? "bg-[var(--accent)] text-[var(--accent-fg)]" : "text-[var(--text-muted)] hover:bg-[var(--bg-hover)]"}`}
              >
                <Monitor className="h-4 w-4" />
                {t("landscape")}
              </button>
              <button
                type="button"
                disabled={!canUpdateMenuBoards || saving || draft.status === "ARCHIVED"}
                onClick={() => setDraft({ ...draft, orientation: "PORTRAIT" })}
                className={`inline-flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-semibold transition ${draft.orientation === "PORTRAIT" ? "bg-[var(--accent)] text-[var(--accent-fg)]" : "text-[var(--text-muted)] hover:bg-[var(--bg-hover)]"}`}
              >
                <Smartphone className="h-4 w-4" />
                {t("portrait")}
              </button>
            </div>
          </div>
            <div ref={previewViewportRef} className={`grid w-full place-items-center overflow-hidden rounded-xl bg-[var(--bg-elevated)] ${draft.orientation === "LANDSCAPE" ? "aspect-video" : "min-h-[620px]"}`}>
              <div
                className="max-h-full max-w-full"
                style={{
                  aspectRatio: draft.orientation === "LANDSCAPE" ? "16 / 9" : "9 / 16",
                  width: previewWidth ? `${previewWidth}px` : undefined,
                  height: previewHeight ? `${previewHeight}px` : undefined,
                }}
              >
                <MenuBoardPreviewSurface board={draft} large fixedRatio />
              </div>
            </div>
          </section>
        </main>
      </div>

      <ConfirmModal
        open={archiveOpen}
        title="Archive menu board"
        description={`Archive ${board.name}? It will move to Archived and stop showing on public displays.`}
        confirmLabel="Archive"
        loading={saving}
        onConfirm={archiveBoard}
        onCancel={() => setArchiveOpen(false)}
      />
      <ConfirmModal
        open={deleteOpen}
        title="Move menu board to Trash"
        description={`Move ${board.name} to Trash? It will stop appearing on displays and can be restored from the Menu Board Trash.`}
        confirmLabel="Move to Trash"
        loading={saving}
        onConfirm={deleteBoard}
        onCancel={() => setDeleteOpen(false)}
      />
    </div>
  );
}

function WorkspaceTopBar({
  draft,
  hasChanges,
  saving,
  canUpdate,
  canPublish,
  canDuplicate,
  canArchive,
  canRestore,
  canDelete,
  onBack,
  onPreview,
  onSave,
  onPublish,
  onDuplicate,
  onArchive,
  onRestore,
  onDelete,
  t,
}: {
  draft: MenuBoard;
  hasChanges: boolean;
  saving: boolean;
  canUpdate: boolean;
  canPublish: boolean;
  canDuplicate: boolean;
  canArchive: boolean;
  canRestore: boolean;
  canDelete: boolean;
  onBack: () => void;
  onPreview: () => void;
  onSave: () => void;
  onPublish: () => void;
  onDuplicate: () => void;
  onArchive: () => void;
  onRestore: () => void;
  onDelete: () => void;
  t: ReturnType<typeof useMenuBoardText>;
}) {
  return (
    <header className="sticky top-0 z-30 flex h-auto flex-col gap-3 border-b border-[var(--border)] bg-[var(--bg-elevated)] px-4 py-3 xl:h-20 xl:flex-row xl:items-center xl:justify-between xl:px-6">
      <div className="flex min-w-0 items-center gap-3">
        <span className="grid h-11 w-11 shrink-0 place-items-center rounded-xl border border-[var(--border)] bg-[var(--bg)] text-[var(--accent)]">
          <MonitorPlay className="h-5 w-5" />
        </span>
        <div className="min-w-0">
          <p className="text-xs font-medium uppercase tracking-wide text-[var(--text-faint)]">{t("menuBoard")}</p>
          <h1 className="truncate text-lg font-semibold text-[var(--text)]">{draft.name}</h1>
          {hasChanges && <p className="text-xs text-[var(--accent)]">{t("unsavedChanges")}</p>}
        </div>
        <Button type="button" variant="secondary" onClick={onBack}>
          <ArrowLeft className="h-4 w-4" />
          {t("back")}
        </Button>
      </div>

      <div className="flex flex-wrap items-center gap-2 xl:justify-end">
        <Button type="button" variant="secondary" onClick={onPreview}>
          <Eye className="h-4 w-4" />
          {t("preview")}
        </Button>
        <Button type="button" variant="secondary" onClick={onDuplicate} disabled={!canDuplicate || saving}>
          <Copy className="h-4 w-4" />
          {t("duplicate")}
        </Button>
        <Button type="button" variant="secondary" onClick={onArchive} disabled={!canArchive || !canUpdate || saving || draft.status === "ARCHIVED"}>
          <Archive className="h-4 w-4" />
          {t("archive")}
        </Button>
        {draft.status === "ARCHIVED" && (
          <Button type="button" variant="secondary" onClick={onRestore} disabled={!canRestore || saving}>
            <RotateCcw className="h-4 w-4" />
            {t("restore")}
          </Button>
        )}
        <Button type="button" variant="danger" onClick={onDelete} disabled={!canDelete || saving}>
          <Trash2 className="h-4 w-4" />
          {t("delete")}
        </Button>
        <Button type="button" variant="secondary" onClick={onSave} disabled={!canUpdate || saving || !hasChanges}>
          {t("save")}
        </Button>
        <Button type="button" onClick={onPublish} disabled={!canUpdate || !canPublish || saving || (draft.status === "PUBLISHED" && !hasChanges)}>
          <Send className="h-4 w-4" />
          {t("publish")}
        </Button>
      </div>
    </header>
  );
}

function DetailSkeleton() {
  return (
    <div className="fixed inset-0 z-[80] overflow-hidden bg-[var(--bg)] text-[var(--text)]">
      <div className="h-20 animate-pulse border-b border-[var(--border)] bg-[var(--bg-elevated)]" />
      <div className="grid gap-4 p-3 sm:p-5 xl:grid-cols-[180px_minmax(420px,500px)_minmax(0,1fr)] xl:gap-5">
        <div className="h-[520px] animate-pulse rounded-xl border border-[var(--border)] bg-[var(--bg-elevated)]" />
        <div className="h-[620px] animate-pulse rounded-xl border border-[var(--border)] bg-[var(--bg-elevated)]" />
        <div className="aspect-video animate-pulse rounded-xl border border-[var(--border)] bg-[var(--bg-elevated)]" />
      </div>
    </div>
  );
}
