import React from "react";
import ReactDOM from "react-dom/client";
import { invoke } from "@tauri-apps/api/core";
import { listen } from "@tauri-apps/api/event";
import {
  PhysicalPosition,
  PhysicalSize,
  currentMonitor,
  getCurrentWindow,
  monitorFromPoint
} from "@tauri-apps/api/window";
import { PixiPetRenderer, type UiSpritePack, type UiSnapshot } from "./renderer/pixi_pet";
import "./styles.css";

type UiSettingsSnapshot = {
  active_sprite_pack: string;
  scale: number;
  visible: boolean;
  always_on_top: boolean;
  tauri_animation_slowdown_factor: number;
};

type UiSpritePackOption = {
  id: string;
  pack_id_or_path: string;
};

type UiApiToken = {
  id: string;
  label: string;
  token: string;
  created_at_ms: number;
};

const WINDOW_PADDING = 16;
const WINDOW_WORKAREA_MARGIN = 80;
const MIN_WINDOW_SIZE = 64;
const SIZE_EPSILON = 0.5;
const SCALE_EPSILON = 0.0001;
const SCALE_MIN = 0.5;
const SCALE_MAX = 3.0;
const LOGICAL_BASE_FRAME_WIDTH = 512;
const LOGICAL_BASE_FRAME_HEIGHT = 512;
const SLOWDOWN_FACTOR_MIN = 1;
const SLOWDOWN_FACTOR_MAX = 20;
const SLOWDOWN_FACTOR_DEFAULT = 3;

async function invokeSetSpritePack(packIdOrPath: string): Promise<UiSnapshot> {
  return invoke<UiSnapshot>("set_sprite_pack", { packIdOrPath });
}

async function invokeSetScale(scale: number): Promise<UiSnapshot> {
  return invoke<UiSnapshot>("set_scale", { scale });
}

async function invokeSetVisibility(visible: boolean): Promise<UiSnapshot> {
  return invoke<UiSnapshot>("set_visibility", { visible });
}

async function invokeSetAlwaysOnTop(alwaysOnTop: boolean): Promise<UiSnapshot> {
  return invoke<UiSnapshot>("set_always_on_top", { alwaysOnTop });
}

async function invokeAnimationSlowdownFactor(): Promise<number> {
  return invoke<number>("tauri_animation_slowdown_factor");
}

async function invokeSetAnimationSlowdownFactor(factor: number): Promise<number> {
  return invoke<number>("set_tauri_animation_slowdown_factor", { factor });
}

async function invokeListApiTokens(): Promise<UiApiToken[]> {
  return invoke<UiApiToken[]>("list_api_tokens");
}

async function invokeCreateApiToken(label?: string): Promise<UiApiToken> {
  return invoke<UiApiToken>("create_api_token", { label });
}

async function invokeRenameApiToken(id: string, label: string): Promise<UiApiToken[]> {
  return invoke<UiApiToken[]>("rename_api_token", { id, label });
}

async function invokeRevokeApiToken(id: string): Promise<UiApiToken[]> {
  return invoke<UiApiToken[]>("revoke_api_token", { id });
}

function fittedWindowSize(
  scale: number
): { width: number; height: number } {
  const safeScale = Number.isFinite(scale) && scale > 0 ? scale : 1;
  const width = Math.round(
    Math.max(LOGICAL_BASE_FRAME_WIDTH * safeScale + WINDOW_PADDING, MIN_WINDOW_SIZE)
  );
  const height = Math.round(
    Math.max(LOGICAL_BASE_FRAME_HEIGHT * safeScale + WINDOW_PADDING, MIN_WINDOW_SIZE)
  );
  return { width, height };
}

function effectiveScaleForWindowSize(width: number, height: number): number {
  const availableWidth = Math.max(width - WINDOW_PADDING, MIN_WINDOW_SIZE);
  const availableHeight = Math.max(height - WINDOW_PADDING, MIN_WINDOW_SIZE);
  const scaleByWidth = availableWidth / LOGICAL_BASE_FRAME_WIDTH;
  const scaleByHeight = availableHeight / LOGICAL_BASE_FRAME_HEIGHT;
  const scale = Math.min(scaleByWidth, scaleByHeight);
  return Math.max(SCALE_MIN, Math.min(scale, SCALE_MAX));
}

async function fitWindowForScale(scale: number): Promise<number> {
  const window = getCurrentWindow();
  const [outerPosition, innerSize] = await Promise.all([window.outerPosition(), window.innerSize()]);
  const target = fittedWindowSize(scale);
  const centerX = outerPosition.x + innerSize.width / 2;
  const centerY = outerPosition.y + innerSize.height / 2;
  let targetWidth = target.width;
  let targetHeight = target.height;
  let targetX = centerX - targetWidth / 2;
  let targetY = centerY - targetHeight / 2;
  let monitor:
    | {
        position: { x: number; y: number };
        size: { width: number; height: number };
        workArea: { position: { x: number; y: number }; size: { width: number; height: number } };
      }
    | null = null;

  try {
    monitor = (await monitorFromPoint(centerX, centerY)) ?? (await currentMonitor());
  } catch {
    monitor = null;
  }

  if (monitor !== null) {
    const widthCap = Math.max(
      monitor.workArea.size.width - WINDOW_WORKAREA_MARGIN,
      MIN_WINDOW_SIZE
    );
    const heightCap = Math.max(
      monitor.workArea.size.height - WINDOW_WORKAREA_MARGIN,
      MIN_WINDOW_SIZE
    );
    targetWidth = Math.min(targetWidth, widthCap);
    targetHeight = Math.min(targetHeight, heightCap);
    targetX = centerX - targetWidth / 2;
    targetY = centerY - targetHeight / 2;
  }

  const widthChanged = Math.abs(targetWidth - innerSize.width) > SIZE_EPSILON;
  const heightChanged = Math.abs(targetHeight - innerSize.height) > SIZE_EPSILON;
  if (widthChanged || heightChanged) {
    await window.setSize(new PhysicalSize(targetWidth, targetHeight));
    if (monitor !== null) {
      const minX = Math.round(monitor.workArea.position.x);
      const minY = Math.round(monitor.workArea.position.y);
      const maxX = Math.round(
        monitor.workArea.position.x + monitor.workArea.size.width - targetWidth
      );
      const maxY = Math.round(
        monitor.workArea.position.y + monitor.workArea.size.height - targetHeight
      );
      targetX = maxX < minX ? minX : Math.min(Math.max(targetX, minX), maxX);
      targetY = maxY < minY ? minY : Math.min(Math.max(targetY, minY), maxY);
    }
    await window.setPosition(new PhysicalPosition(Math.round(targetX), Math.round(targetY)));
  }

  return effectiveScaleForWindowSize(targetWidth, targetHeight);
}

function MainOverlayWindow(): JSX.Element {
  const [snapshot, setSnapshot] = React.useState<UiSnapshot | null>(null);
  const [error, setError] = React.useState<string | null>(null);
  const [debugOverlayVisible, setDebugOverlayVisible] = React.useState(false);
  const hostRef = React.useRef<HTMLDivElement | null>(null);
  const rendererRef = React.useRef<PixiPetRenderer | null>(null);
  const activePackRef = React.useRef<UiSpritePack | null>(null);
  const loadedPackKeyRef = React.useRef<string | null>(null);
  const effectiveScaleSyncRef = React.useRef<number | null>(null);
  const slowdownFactorRef = React.useRef<number>(SLOWDOWN_FACTOR_DEFAULT);
  const loadingPackRef = React.useRef(false);
  const mountedRef = React.useRef(false);

  React.useEffect(() => {
    mountedRef.current = true;
    let unlisten: null | (() => void) = null;

    const recreateRenderer = async (
      pack: UiSpritePack,
      nextSnapshot: UiSnapshot
    ): Promise<boolean> => {
      if (!mountedRef.current || hostRef.current === null) {
        return false;
      }
      const previousRenderer = rendererRef.current;
      const nextRenderer = await PixiPetRenderer.create(hostRef.current, pack, nextSnapshot);
      nextRenderer.setAnimationSlowdownFactor(slowdownFactorRef.current);
      rendererRef.current = nextRenderer;
      activePackRef.current = pack;
      loadedPackKeyRef.current = nextSnapshot.active_sprite_pack;
      previousRenderer?.dispose();
      return true;
    };

    const tryFitWindow = async (scale: number): Promise<number | null> => {
      try {
        return await fitWindowForScale(scale);
      } catch (err) {
        if (mountedRef.current) {
          setError(String(err));
        }
        return null;
      }
    };

    const syncEffectiveScale = async (snapshotScale: number, effectiveScale: number): Promise<void> => {
      if (Math.abs(snapshotScale - effectiveScale) < SCALE_EPSILON) {
        return;
      }
      if (
        effectiveScaleSyncRef.current !== null &&
        Math.abs(effectiveScaleSyncRef.current - effectiveScale) < SCALE_EPSILON
      ) {
        return;
      }
      effectiveScaleSyncRef.current = effectiveScale;
      try {
        await invokeSetScale(effectiveScale);
      } catch (err) {
        if (mountedRef.current) {
          setError(String(err));
        }
      }
    };

    const processSnapshot = async (value: UiSnapshot): Promise<void> => {
      if (!mountedRef.current) {
        return;
      }
      if (
        effectiveScaleSyncRef.current !== null &&
        Math.abs(effectiveScaleSyncRef.current - value.scale) < SCALE_EPSILON
      ) {
        effectiveScaleSyncRef.current = null;
      }
      setSnapshot(value);
      rendererRef.current?.applySnapshot(value);

      const activePack = activePackRef.current;
      const needsReload =
        activePack === null || loadedPackKeyRef.current !== value.active_sprite_pack;
      if (needsReload && !loadingPackRef.current) {
        loadingPackRef.current = true;
        let reloaded = false;
        try {
            const pack = await invoke<UiSpritePack>("load_active_sprite_pack");
            reloaded = await recreateRenderer(pack, value);
            if (reloaded) {
            const effectiveScale = await tryFitWindow(value.scale);
              if (effectiveScale !== null) {
                await syncEffectiveScale(value.scale, effectiveScale);
              }
            if (mountedRef.current && effectiveScale !== null) {
              setError(null);
            }
          }
        } catch (err) {
          if (mountedRef.current) {
            console.error("reload_pack_failed", err);
            setError(String(err));
          }
        } finally {
          loadingPackRef.current = false;
        }
      }

      if (activePackRef.current === null) {
        return;
      }
      const effectiveScale = await tryFitWindow(value.scale);
      if (effectiveScale !== null) {
        await syncEffectiveScale(value.scale, effectiveScale);
      }
      if (effectiveScale !== null && mountedRef.current) {
        setError(null);
      }
    };

    Promise.all([
      invoke<UiSpritePack>("load_active_sprite_pack"),
      invoke<UiSnapshot>("current_state"),
      invoke<boolean>("debug_overlay_visible"),
      invokeAnimationSlowdownFactor()
    ])
      .then(async ([pack, initialSnapshot, showDebug, slowdownFactor]) => {
        if (!mountedRef.current) {
          return;
        }
        setDebugOverlayVisible(showDebug);
        slowdownFactorRef.current = Math.min(
          Math.max(Math.round(slowdownFactor), SLOWDOWN_FACTOR_MIN),
          SLOWDOWN_FACTOR_MAX
        );
        await recreateRenderer(pack, initialSnapshot);
        await processSnapshot(initialSnapshot);

        const unlistenSnapshot = await listen<UiSnapshot>("runtime:snapshot", (event) => {
          void processSnapshot(event.payload);
        });
        const unlistenDebug = await listen<boolean>("runtime:debug-overlay-visible", (event) => {
          if (!mountedRef.current) {
            return;
          }
          setDebugOverlayVisible(Boolean(event.payload));
        });
        const unlistenSlowdown = await listen<number>(
          "runtime:animation-slowdown-factor",
          (event) => {
            if (!mountedRef.current) {
              return;
            }
            slowdownFactorRef.current = Math.min(
              Math.max(Math.round(Number(event.payload)), SLOWDOWN_FACTOR_MIN),
              SLOWDOWN_FACTOR_MAX
            );
            rendererRef.current?.setAnimationSlowdownFactor(slowdownFactorRef.current);
          }
        );
        unlisten = () => {
          unlistenSnapshot();
          unlistenDebug();
          unlistenSlowdown();
        };
      })
      .catch((err) => {
        if (mountedRef.current) {
          setError(String(err));
        }
      });

    return () => {
      mountedRef.current = false;
      if (unlisten !== null) {
        unlisten();
      }
      rendererRef.current?.dispose();
      rendererRef.current = null;
    };
  }, []);

  const toggleDebugOverlay = React.useCallback(async () => {
    try {
      const next = !debugOverlayVisible;
      const persisted = await invoke<boolean>("set_debug_overlay_visible", {
        visible: next
      });
      setDebugOverlayVisible(persisted);
    } catch (err) {
      setError(String(err));
    }
  }, [debugOverlayVisible]);

  React.useEffect(() => {
    const onKeyDown = (event: KeyboardEvent): void => {
      if (!event.ctrlKey || !event.shiftKey || event.code !== "KeyD") {
        return;
      }
      event.preventDefault();
      void toggleDebugOverlay();
    };
    window.addEventListener("keydown", onKeyDown);
    return () => {
      window.removeEventListener("keydown", onKeyDown);
    };
  }, [toggleDebugOverlay]);

  const onMouseDown = React.useCallback((event: React.MouseEvent<HTMLElement>) => {
    if (event.button !== 0) {
      return;
    }
    void getCurrentWindow().startDragging().catch((err) => {
      setError(String(err));
    });
  }, []);

  return (
    <main className="app overlay-app" onMouseDown={onMouseDown}>
      <div className="canvas-host" ref={hostRef} />
      {error !== null && !debugOverlayVisible ? <p className="error-banner">{error}</p> : null}
      {debugOverlayVisible ? (
        <section className="debug-panel">
          <h1>sprimo-tauri</h1>
          <p className="hint">Toggle: Ctrl+Shift+D</p>
          {error !== null ? <p className="error">{error}</p> : null}
          {snapshot === null ? (
            <p>Loading snapshot...</p>
          ) : (
            <dl>
              <dt>state</dt>
              <dd>{snapshot.state}</dd>
              <dt>animation</dt>
              <dd>{snapshot.current_animation}</dd>
              <dt>pack</dt>
              <dd>{snapshot.active_sprite_pack}</dd>
              <dt>position</dt>
              <dd>
                {snapshot.x}, {snapshot.y}
              </dd>
              <dt>scale</dt>
              <dd>{snapshot.scale}</dd>
            </dl>
          )}
        </section>
      ) : null}
    </main>
  );
}

function SettingsWindow(): JSX.Element {
  const [settings, setSettings] = React.useState<UiSettingsSnapshot | null>(null);
  const [packs, setPacks] = React.useState<UiSpritePackOption[]>([]);
  const [tokens, setTokens] = React.useState<UiApiToken[]>([]);
  const [tokenDrafts, setTokenDrafts] = React.useState<Record<string, string>>({});
  const [newTokenLabel, setNewTokenLabel] = React.useState("");
  const [activePack, setActivePack] = React.useState<UiSpritePack | null>(null);
  const [error, setError] = React.useState<string | null>(null);
  const [pending, setPending] = React.useState(false);

  React.useEffect(() => {
    let unlisten: null | (() => void) = null;
    let mounted = true;
    Promise.all([
      invoke<UiSettingsSnapshot>("settings_snapshot"),
      invoke<UiSpritePackOption[]>("list_sprite_packs"),
      invoke<UiSpritePack>("load_active_sprite_pack"),
      invokeListApiTokens()
    ])
      .then(async ([snapshot, options, pack, authTokens]) => {
        if (!mounted) {
          return;
        }
        setSettings(snapshot);
        setPacks(options);
        setActivePack(pack);
        setTokens(authTokens);
        setTokenDrafts(
          authTokens.reduce<Record<string, string>>((acc, token) => {
            acc[token.id] = token.label;
            return acc;
          }, {})
        );
        unlisten = await listen<UiSnapshot>("runtime:snapshot", (event) => {
          if (!mounted) {
            return;
          }
          const payload = event.payload;
          if (payload.active_sprite_pack !== activePack?.id) {
            void invoke<UiSpritePack>("load_active_sprite_pack")
              .then((nextPack) => {
                if (mounted) {
                  setActivePack(nextPack);
                }
              })
              .catch(() => {
                // Keep existing pack metadata if reload fails.
              });
          }
          setSettings((prev) => {
            if (prev === null) {
              return prev;
            }
            return {
              active_sprite_pack: payload.active_sprite_pack,
              scale: payload.scale,
              visible: payload.visible,
              always_on_top: payload.always_on_top,
              tauri_animation_slowdown_factor:
                prev.tauri_animation_slowdown_factor ?? SLOWDOWN_FACTOR_DEFAULT
            };
          });
        });
        const unlistenSlowdown = await listen<number>("runtime:animation-slowdown-factor", (event) => {
          if (!mounted) {
            return;
          }
          const factor = Math.min(
            Math.max(Math.round(Number(event.payload)), SLOWDOWN_FACTOR_MIN),
            SLOWDOWN_FACTOR_MAX
          );
          setSettings((prev) =>
            prev === null ? prev : { ...prev, tauri_animation_slowdown_factor: factor }
          );
        });
        const previousUnlisten = unlisten;
        unlisten = () => {
          previousUnlisten();
          unlistenSlowdown();
        };
      })
      .catch((err) => {
        if (mounted) {
          setError(String(err));
        }
      });
    return () => {
      mounted = false;
      if (unlisten !== null) {
        unlisten();
      }
    };
  }, [activePack?.id]);

  const withPending = React.useCallback(async <T,>(fn: () => Promise<T>): Promise<T | null> => {
    setPending(true);
    setError(null);
    try {
      return await fn();
    } catch (err) {
      setError(String(err));
      return null;
    } finally {
      setPending(false);
    }
  }, []);

  const onPackChange = React.useCallback(
    async (event: React.ChangeEvent<HTMLSelectElement>) => {
      const value = event.target.value;
      const next = await withPending(() => invokeSetSpritePack(value));
      if (next === null) {
        return;
      }
      const refreshedPack = await withPending(() => invoke<UiSpritePack>("load_active_sprite_pack"));
      if (refreshedPack !== null) {
        setActivePack(refreshedPack);
      }
      setSettings((prev) =>
        prev === null
          ? prev
          : {
              ...prev,
              active_sprite_pack: next.active_sprite_pack
            }
      );
    },
    [withPending]
  );

  const onScaleChange = React.useCallback(
    async (event: React.ChangeEvent<HTMLInputElement>) => {
      const value = Number(event.target.value);
      if (!Number.isFinite(value)) {
        return;
      }
      const next = await withPending(() => invokeSetScale(value));
      if (next === null) {
        return;
      }
      setSettings((prev) => (prev === null ? prev : { ...prev, scale: next.scale }));
    },
    [withPending]
  );

  const onVisibleChange = React.useCallback(
    async (event: React.ChangeEvent<HTMLInputElement>) => {
      const value = event.target.checked;
      const next = await withPending(() => invokeSetVisibility(value));
      if (next === null) {
        return;
      }
      setSettings((prev) => (prev === null ? prev : { ...prev, visible: value }));
    },
    [withPending]
  );

  const onAlwaysOnTopChange = React.useCallback(
    async (event: React.ChangeEvent<HTMLInputElement>) => {
      const value = event.target.checked;
      const next = await withPending(() => invokeSetAlwaysOnTop(value));
      if (next === null) {
        return;
      }
      setSettings((prev) => (prev === null ? prev : { ...prev, always_on_top: value }));
    },
    [withPending]
  );

  const onAnimationSlowdownFactorChange = React.useCallback(
    async (event: React.ChangeEvent<HTMLInputElement>) => {
      const value = Number(event.target.value);
      if (!Number.isFinite(value)) {
        return;
      }
      const clamped = Math.min(
        Math.max(Math.round(value), SLOWDOWN_FACTOR_MIN),
        SLOWDOWN_FACTOR_MAX
      );
      const persisted = await withPending(() => invokeSetAnimationSlowdownFactor(clamped));
      if (persisted === null) {
        return;
      }
      setSettings((prev) =>
        prev === null
          ? prev
          : { ...prev, tauri_animation_slowdown_factor: Number(persisted) }
      );
    },
    [withPending]
  );

  const onNewTokenLabelChange = React.useCallback(
    (event: React.ChangeEvent<HTMLInputElement>) => {
      setNewTokenLabel(event.target.value);
    },
    []
  );

  const onCreateToken = React.useCallback(async () => {
    const created = await withPending(() => invokeCreateApiToken(newTokenLabel || undefined));
    if (created === null) {
      return;
    }
    const refreshed = await withPending(() => invokeListApiTokens());
    if (refreshed === null) {
      return;
    }
    setTokens(refreshed);
    setTokenDrafts(
      refreshed.reduce<Record<string, string>>((acc, token) => {
        acc[token.id] = token.label;
        return acc;
      }, {})
    );
    setNewTokenLabel("");
  }, [newTokenLabel, withPending]);

  const onTokenDraftChange = React.useCallback((id: string, value: string) => {
    setTokenDrafts((prev) => ({
      ...prev,
      [id]: value
    }));
  }, []);

  const onRenameToken = React.useCallback(
    async (id: string) => {
      const nextLabel = tokenDrafts[id] ?? "";
      const refreshed = await withPending(() => invokeRenameApiToken(id, nextLabel));
      if (refreshed === null) {
        return;
      }
      setTokens(refreshed);
      setTokenDrafts(
        refreshed.reduce<Record<string, string>>((acc, token) => {
          acc[token.id] = token.label;
          return acc;
        }, {})
      );
    },
    [tokenDrafts, withPending]
  );

  const onCopyToken = React.useCallback(async (token: string) => {
    try {
      await navigator.clipboard.writeText(token);
    } catch (err) {
      setError(String(err));
    }
  }, []);

  const onRevokeToken = React.useCallback(
    async (id: string) => {
      if (!window.confirm("Revoke this API token?")) {
        return;
      }
      const refreshed = await withPending(() => invokeRevokeApiToken(id));
      if (refreshed === null) {
        return;
      }
      setTokens(refreshed);
      setTokenDrafts(
        refreshed.reduce<Record<string, string>>((acc, token) => {
          acc[token.id] = token.label;
          return acc;
        }, {})
      );
    },
    [withPending]
  );

  return (
    <main className="settings-root">
      <section className="settings-card">
        <h1>Settings</h1>
        <p className="settings-subtitle">Character and window controls</p>
        {error !== null ? <p className="settings-error">{error}</p> : null}
        {settings === null ? (
          <p>Loading settings...</p>
        ) : (
          <>
            <label className="field">
              <span>Character</span>
              <select
                value={settings.active_sprite_pack}
                disabled={pending}
                onChange={onPackChange}
              >
                {packs.map((pack) => (
                  <option key={pack.pack_id_or_path} value={pack.pack_id_or_path}>
                    {pack.id}
                  </option>
                ))}
              </select>
            </label>
            <label className="field">
              <span>Scale: {settings.scale.toFixed(2)}x</span>
              <input
                type="range"
                min={SCALE_MIN}
                max={SCALE_MAX}
                step={0.05}
                value={settings.scale}
                disabled={pending}
                onChange={onScaleChange}
              />
            </label>
            <label className="field">
              <span>
                Animation Speed Factor: x{settings.tauri_animation_slowdown_factor}
              </span>
              <input
                type="range"
                min={SLOWDOWN_FACTOR_MIN}
                max={SLOWDOWN_FACTOR_MAX}
                step={1}
                value={settings.tauri_animation_slowdown_factor}
                disabled={pending}
                onChange={onAnimationSlowdownFactorChange}
              />
            </label>
            <label className="toggle">
              <input
                type="checkbox"
                checked={settings.visible}
                disabled={pending}
                onChange={onVisibleChange}
              />
              <span>Visible</span>
            </label>
            <label className="toggle">
              <input
                type="checkbox"
                checked={settings.always_on_top}
                disabled={pending}
                onChange={onAlwaysOnTopChange}
              />
              <span>Always on top</span>
            </label>
            <section className="token-section">
              <h2>API Tokens</h2>
              <p className="token-help">
                Use any listed token as `Authorization: Bearer &lt;token&gt;`.
              </p>
              <div className="token-create">
                <input
                  type="text"
                  value={newTokenLabel}
                  placeholder="New token label"
                  disabled={pending}
                  onChange={onNewTokenLabelChange}
                />
                <button type="button" disabled={pending} onClick={onCreateToken}>
                  Create token
                </button>
              </div>
              <div className="token-list">
                {tokens.map((entry) => (
                  <article className="token-item" key={entry.id}>
                    <label className="field">
                      <span>Label</span>
                      <input
                        type="text"
                        value={tokenDrafts[entry.id] ?? ""}
                        disabled={pending}
                        onChange={(event) =>
                          onTokenDraftChange(entry.id, event.target.value)
                        }
                      />
                    </label>
                    <label className="field">
                      <span>Token</span>
                      <input type="text" value={entry.token} readOnly />
                    </label>
                    <div className="token-actions">
                      <button
                        type="button"
                        disabled={pending}
                        onClick={() => onCopyToken(entry.token)}
                      >
                        Copy
                      </button>
                      <button
                        type="button"
                        disabled={pending}
                        onClick={() => onRenameToken(entry.id)}
                      >
                        Save label
                      </button>
                      <button
                        type="button"
                        disabled={pending || tokens.length <= 1}
                        onClick={() => onRevokeToken(entry.id)}
                      >
                        Revoke
                      </button>
                    </div>
                  </article>
                ))}
              </div>
            </section>
          </>
        )}
      </section>
    </main>
  );
}

function AppRoot(): JSX.Element {
  const windowLabel = getCurrentWindow().label;
  if (windowLabel === "settings") {
    return <SettingsWindow />;
  }
  return <MainOverlayWindow />;
}

ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <AppRoot />
  </React.StrictMode>
);
