/**
 * `ViewWindow` — the panel-only entry (ticket #23, reworked in #47).
 *
 * A detached OS window loads the same SPA with `?panel=<panel>` (no project id);
 * {@link main} routes to this component instead of the full {@link App} when a
 * `panel` param is present. It re-instantiates the DI (same adapters/gateways as
 * the main window — views are gateway/read-model-driven, so no heavy state is
 * transferred) and renders **only** the requested view for the project the main
 * window currently has in focus.
 *
 * Focus, not embedding (#47): the window never carries — nor reopens — a project
 * id. It reads the current focus from the {@link FocusedProjectGateway} at mount
 * and then tracks {@link FocusedProjectGateway.onFocusedProjectChanged}. While no
 * project is focused it mounts **no** project-scoped panel and shows an "open a
 * project" shell, so a window restored at restart never surfaces a stale
 * project's data.
 *
 * Pure presentation over the ports: no `invoke()`, no cross-window messaging,
 * and crucially no `openProject()` — opening a project is the main window's job.
 */

import { useEffect, useMemo, useState } from "react";

import type { CustomPluginLayoutCell } from "@/domain";
import type { FocusedProject } from "@/ports";
import { Panel } from "@/shared";
import { PANEL_TITLE, type PanelId } from "@/features/projects";
import { ViewPanelBody, type ViewPanelId } from "@/features/projects";
import {
  PluginLayoutCellView,
  PluginRuntimeProvider,
  usePluginRuntime,
  type PluginRuntimeContextValue,
} from "@/features/plugins";
import { useGateways } from "./di";

/** Panels that may be shown in a detached window (everything but "projects"). */
const DETACHABLE = new Set<string>(
  (Object.keys(PANEL_TITLE) as PanelId[]).filter((p) => p !== "projects"),
);

/** Parsed `?panel=` params, or `null` when absent/invalid. */
export interface ViewWindowParams {
  panel: ViewPanelId;
}

export interface PluginLayoutWindowParams {
  pluginId: string;
  layoutType: string;
  state: unknown;
}

export type DetachedWindowParams =
  | { kind: "panel"; panel: ViewPanelId }
  | { kind: "pluginLayout"; plugin: PluginLayoutWindowParams };

function decodeBase64UrlPart(value: string): string | null {
  try {
    const padded = value.replace(/-/g, "+").replace(/_/g, "/").padEnd(
      Math.ceil(value.length / 4) * 4,
      "=",
    );
    const binary = atob(padded);
    const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
    return new TextDecoder().decode(bytes);
  } catch {
    return null;
  }
}

function decodeBase64UrlJson(value: string | null): unknown {
  if (!value) return null;
  const decoded = decodeBase64UrlPart(value);
  if (!decoded) return null;
  try {
    return JSON.parse(decoded);
  } catch {
    return null;
  }
}

/** Reads and validates the panel-only param from a query string (#47: no project). */
export function parseViewWindowParams(
  search: string,
): DetachedWindowParams | null {
  const params = new URLSearchParams(search);
  if (params.get("pluginLayout") === "1") {
    const pluginId = decodeBase64UrlPart(params.get("pluginId") ?? "");
    const layoutType = decodeBase64UrlPart(params.get("layoutType") ?? "");
    if (!pluginId || !layoutType) return null;
    return {
      kind: "pluginLayout",
      plugin: {
        pluginId,
        layoutType,
        state: decodeBase64UrlJson(params.get("state")),
      },
    };
  }

  const panel = params.get("panel");
  if (!panel || !DETACHABLE.has(panel)) return null;
  return { kind: "panel", panel: panel as ViewPanelId };
}

export interface ViewWindowProps {
  panel: ViewPanelId;
}

export function ViewWindow({ panel }: ViewWindowProps) {
  const { focusedProject } = useGateways();
  // The focused project this window follows. `undefined` = not yet resolved
  // (initial read in flight); `null` = resolved, no project focused.
  const [focus, setFocus] = useState<FocusedProject | null | undefined>(
    undefined,
  );

  // Read the current focus at mount, then track changes. Never opens a project:
  // the window is a pure follower of the main window's focus (#47).
  useEffect(() => {
    let cancelled = false;
    let unsubscribe: (() => void) | undefined;
    focusedProject
      .getFocusedProject()
      .then((project) => {
        if (!cancelled) setFocus(project);
      })
      .catch(() => {
        if (!cancelled) setFocus(null);
      });
    void focusedProject
      .onFocusedProjectChanged((project) => {
        if (!cancelled) setFocus(project);
      })
      .then((un) => {
        if (cancelled) un();
        else unsubscribe = un;
      });
    return () => {
      cancelled = true;
      unsubscribe?.();
    };
  }, [focusedProject]);

  return (
    <div className="flex h-full flex-col bg-canvas text-content">
      <header className="flex shrink-0 items-center gap-2 border-b border-border px-4 py-2.5">
        <h1 className="text-sm font-semibold text-content">
          {PANEL_TITLE[panel]}
        </h1>
        {focus && (
          <span className="truncate text-xs text-muted">· {focus.name}</span>
        )}
      </header>
      <main className="min-h-0 flex-1 overflow-auto p-4">
        {focus ? (
          // Only mount a project-scoped panel when a project is actually focused
          // — the panels require a real `projectRoot`, never a placeholder.
          <ViewPanelBody
            panel={panel}
            projectId={focus.id}
            projectRoot={focus.root}
          />
        ) : (
          <Panel>
            <p className="text-sm text-muted">
              Ouvrez un projet dans la fenêtre principale pour afficher ce
              panneau.
            </p>
          </Panel>
        )}
      </main>
    </div>
  );
}

export interface PluginLayoutWindowProps {
  plugin: PluginLayoutWindowParams;
  runtimeValue?: PluginRuntimeContextValue;
}

export function PluginLayoutWindow({ plugin, runtimeValue }: PluginLayoutWindowProps) {
  return (
    <PluginRuntimeProvider value={runtimeValue}>
      <PluginLayoutWindowBody plugin={plugin} />
    </PluginRuntimeProvider>
  );
}

function PluginLayoutWindowBody({ plugin }: PluginLayoutWindowProps) {
  const { focusedProject } = useGateways();
  const { registry, loading } = usePluginRuntime();
  const [focus, setFocus] = useState<FocusedProject | null | undefined>(undefined);
  const [state, setState] = useState<unknown>(plugin.state);
  const title =
    registry
      .get(plugin.pluginId)
      ?.contributes.layouts.find((layout) => layout.type === plugin.layoutType)?.label ??
    plugin.layoutType;
  const cell = useMemo<CustomPluginLayoutCell>(
    () => ({
      id: `plugin-window:${plugin.pluginId}:${plugin.layoutType}`,
      pluginId: plugin.pluginId,
      layoutType: plugin.layoutType,
      state,
    }),
    [plugin.layoutType, plugin.pluginId, state],
  );

  useEffect(() => {
    let cancelled = false;
    let unsubscribe: (() => void) | undefined;
    focusedProject
      .getFocusedProject()
      .then((project) => {
        if (!cancelled) setFocus(project);
      })
      .catch(() => {
        if (!cancelled) setFocus(null);
      });
    void focusedProject
      .onFocusedProjectChanged((project) => {
        if (!cancelled) setFocus(project);
      })
      .then((un) => {
        if (cancelled) un();
        else unsubscribe = un;
      });
    return () => {
      cancelled = true;
      unsubscribe?.();
    };
  }, [focusedProject]);

  return (
    <div className="flex h-full flex-col bg-canvas text-content">
      <header className="flex shrink-0 items-center gap-2 border-b border-border px-4 py-2.5">
        <h1 className="text-sm font-semibold text-content">{title}</h1>
        <span className="truncate text-xs text-muted">
          · {registry.get(plugin.pluginId)?.displayName ?? plugin.pluginId}
        </span>
        {focus && <span className="truncate text-xs text-muted">· {focus.name}</span>}
      </header>
      <main className="min-h-0 flex-1 overflow-auto p-4">
        {focus ? (
          <PluginLayoutCellView
            projectId={focus.id}
            cell={cell}
            onStateChange={setState}
            onOpenPlugins={() => {}}
            onChooseAnotherLayout={() => {}}
          />
        ) : (
          <Panel>
            <p className="text-sm text-muted">
              Ouvrez un projet dans la fenêtre principale pour afficher ce layout.
            </p>
          </Panel>
        )}
        {loading && (
          <p className="mt-3 text-xs text-muted">Chargement des plugins...</p>
        )}
      </main>
    </div>
  );
}
