/**
 * Electrobun Bun process — thin router that delegates to domain handler modules.
 *
 * Fix #15: Extracted handlers into src/bun/handlers/ for SRP.
 * Fix #2: Path traversal guard on files.list/read/write via path-guard.ts.
 */

import { BrowserWindow, BrowserView, Updater, Electrobun } from "electrobun/bun";
import { PtyClient } from "./pty-client.ts";
import { settingsDb } from "./settings-db.ts";
import { sessionDb } from "./session-db.ts";
import { btmsgDb } from "./btmsg-db.ts";
import { createBttaskDb } from "./bttask-db.ts";
import { SidecarManager } from "./sidecar-manager.ts";
import type { PtyRPCSchema } from "../shared/pty-rpc-schema.ts";
import { SearchDb } from "./search-db.ts";
import { RelayClient } from "./relay-client.ts";
import { initTelemetry, telemetry } from "./telemetry.ts";

// Handler modules
import { createPtyHandlers } from "./handlers/pty-handlers.ts";
import { createFilesHandlers } from "./handlers/files-handlers.ts";
import { createSettingsHandlers } from "./handlers/settings-handlers.ts";
import { createAgentHandlers } from "./handlers/agent-handlers.ts";
import { createBtmsgHandlers, createBttaskHandlers } from "./handlers/btmsg-handlers.ts";
import { createSearchHandlers } from "./handlers/search-handlers.ts";
import { createPluginHandlers } from "./handlers/plugin-handlers.ts";
import { createRemoteHandlers } from "./handlers/remote-handlers.ts";
import { createGitHandlers } from "./handlers/git-handlers.ts";
import { createProviderHandlers } from "./handlers/provider-handlers.ts";
import { createMiscHandlers } from "./handlers/misc-handlers.ts";

/** Current app version — sourced from electrobun.config.ts at build time. */
const APP_VERSION = "0.0.1";

const DEV_SERVER_PORT = 9760;
const DEV_SERVER_URL = `http://localhost:${DEV_SERVER_PORT}`;

// ── Services ───────────────────────────────────────────────────────────────

const ptyClient = new PtyClient();
const sidecarManager = new SidecarManager();
const searchDb = new SearchDb();
const relayClient = new RelayClient();
const bttaskDb = createBttaskDb(btmsgDb.getHandle());

initTelemetry();

async function connectToDaemon(retries = 5, delayMs = 500): Promise<boolean> {
  for (let attempt = 1; attempt <= retries; attempt++) {
    try {
      await ptyClient.connect();
      console.log("[agor-ptyd] Connected to PTY daemon");
      return true;
    } catch (err) {
      const msg = err instanceof Error ? err.message : String(err);
      if (attempt < retries) {
        console.warn(`[agor-ptyd] Connect attempt ${attempt}/${retries} failed: ${msg}. Retrying in ${delayMs}ms...`);
        await new Promise((r) => setTimeout(r, delayMs));
        delayMs = Math.min(delayMs * 2, 4000);
      } else {
        console.error(`[agor-ptyd] Could not connect after ${retries} attempts: ${msg}`);
      }
    }
  }
  return false;
}

// ── Build handler maps ───────────────────────────────────────────────────

// Placeholder rpc for agent handler — will be set after rpc creation
const rpcRef: { send: Record<string, (...args: unknown[]) => void> } = { send: {} };

const ptyHandlers = createPtyHandlers(ptyClient);
const filesHandlers = createFilesHandlers();
const settingsHandlers = createSettingsHandlers(settingsDb);
const agentHandlers = createAgentHandlers(sidecarManager, sessionDb, rpcRef);
const btmsgHandlers = createBtmsgHandlers(btmsgDb, rpcRef);
const bttaskHandlers = createBttaskHandlers(bttaskDb, rpcRef);
const searchHandlers = createSearchHandlers(searchDb);
const pluginHandlers = createPluginHandlers();
const remoteHandlers = createRemoteHandlers(relayClient, settingsDb);
const gitHandlers = createGitHandlers();
const providerHandlers = createProviderHandlers();
const miscHandlers = createMiscHandlers({
  settingsDb, ptyClient, relayClient, sidecarManager, telemetry, appVersion: APP_VERSION,
});

// Window ref — handlers use closure; set after mainWindow creation
let mainWindow: BrowserWindow;

// ── RPC definition ─────────────────────────────────────────────────────────

const rpc = BrowserView.defineRPC<PtyRPCSchema>({
  maxRequestTime: 120_000,
  handlers: {
    requests: {
      ...ptyHandlers,
      ...filesHandlers,
      ...settingsHandlers,
      ...agentHandlers,
      ...btmsgHandlers,
      ...bttaskHandlers,
      ...searchHandlers,
      ...pluginHandlers,
      ...remoteHandlers,
      ...gitHandlers,
      ...providerHandlers,
      ...miscHandlers,

      // GTK native drag/resize — delegates to window manager (zero CPU)
      "window.beginResize": ({ edge }: { edge: string }) => {
        try {
          // Uses native C library — stored mouse state from real GTK event
          const { startNativeResize } = require("./native-resize.ts");
          const ok = startNativeResize(edge);
          return { ok };
        } catch (err) { console.error("[window.beginResize]", err); return { ok: false }; }
      },
      "window.beginMove": ({ button, rootX, rootY }: { button: number; rootX: number; rootY: number }) => {
        try {
          const { beginMoveDrag } = require("./gtk-window.ts");
          const ok = beginMoveDrag((mainWindow as any).ptr, button, rootX, rootY);
          return { ok };
        } catch (err) { console.error("[window.beginMove]", err); return { ok: false }; }
      },

      // Window controls — need mainWindow closure, stay inline
      "window.minimize": () => { try { mainWindow.minimize(); return { ok: true }; } catch (err) { console.error("[window.minimize]", err); return { ok: false }; } },
      "window.maximize": () => {
        try {
          const frame = mainWindow.getFrame();
          if (frame.x <= 0 && frame.y <= 0) mainWindow.unmaximize(); else mainWindow.maximize();
          return { ok: true };
        } catch (err) { console.error("[window.maximize]", err); return { ok: false }; }
      },
      "window.close": () => { try { mainWindow.close(); return { ok: true }; } catch (err) { console.error("[window.close]", err); return { ok: false }; } },
      "window.getFrame": () => { try { return mainWindow.getFrame(); } catch { return { x: 0, y: 0, width: 1400, height: 900 }; } },
      "window.setPosition": ({ x, y }: { x: number; y: number }) => { try { mainWindow.setPosition(x, y); return { ok: true }; } catch { return { ok: false }; } },
      "window.setFrame": ({ x, y, width, height }: { x: number; y: number; width: number; height: number }) => {
        try {
          mainWindow.setPosition(x, y);
          mainWindow.setSize(width, height);
          return { ok: true };
        } catch (err) { console.error("[window.setFrame]", err); return { ok: false }; }
      },
      "window.clearMinSize": () => {
        try {
          const { ensureResizable } = require("./gtk-window.ts");
          ensureResizable((mainWindow as any).ptr);
          return { ok: true };
        } catch (err) { console.error("[window.clearMinSize]", err); return { ok: false }; }
      },
      "window.gtkSetFrame": ({ x, y, width, height }: { x: number; y: number; width: number; height: number }) => {
        try {
          const { gtkSetFrame } = require("./gtk-window.ts");
          const ok = gtkSetFrame((mainWindow as any).ptr, x, y, width, height);
          return { ok };
        } catch (err) { console.error("[window.gtkSetFrame]", err); return { ok: false }; }
      },
      "window.x11SetFrame": ({ x, y, width, height }: { x: number; y: number; width: number; height: number }) => {
        try {
          const { x11SetFrame } = require("./gtk-window.ts");
          const ok = x11SetFrame((mainWindow as any).ptr, x, y, width, height);
          return { ok };
        } catch (err) { console.error("[window.x11SetFrame]", err); return { ok: false }; }
      },
    },
    messages: {},
  },
});

// Wire rpcRef so agent handlers can forward events to webview
rpcRef.send = rpc.send;

// ── Forward daemon events to WebView ──────────────────────────────────────

ptyClient.on("session_output", (msg) => {
  if (msg.type !== "session_output") return;
  try { rpc.send["pty.output"]({ sessionId: msg.session_id, data: msg.data }); }
  catch (err) { console.error("[pty.output] forward error:", err); }
});

ptyClient.on("session_closed", (msg) => {
  if (msg.type !== "session_closed") return;
  try { rpc.send["pty.closed"]({ sessionId: msg.session_id, exitCode: msg.exit_code }); }
  catch (err) { console.error("[pty.closed] forward error:", err); }
});

// ── Forward relay events to WebView ───────────────────────────────────────

relayClient.onEvent((machineId, event) => {
  try { rpc.send["remote.event"]({ machineId, eventType: event.type, sessionId: event.sessionId, payload: event.payload }); }
  catch (err) { console.error("[remote.event] forward error:", err); }
});

relayClient.onStatus((machineId, status, error) => {
  try { rpc.send["remote.statusChange"]({ machineId, status, error }); }
  catch (err) { console.error("[remote.statusChange] forward error:", err); }
});

// ── App window ────────────────────────────────────────────────────────────

async function getMainViewUrl(): Promise<string> {
  // TEMPORARY: load resize test stub via Vite dev server (keeps RPC bridge)
  const RESIZE_TEST = true; // STUB MODE — minimal page for resize testing
  if (RESIZE_TEST) {
    const testUrl = DEV_SERVER_URL + "/resize-test.html";
    console.log(`[RESIZE_TEST] Loading stub via Vite: ${testUrl}`);
    return testUrl;
  }
  const channel = await Updater.localInfo.channel();
  if (channel === "dev") {
    try {
      await fetch(DEV_SERVER_URL, { method: "HEAD" });
      console.log(`HMR enabled: Using Vite dev server at ${DEV_SERVER_URL}`);
      return DEV_SERVER_URL;
    } catch {
      console.log("Vite dev server not running. Run 'bun run dev:hmr' for HMR support.");
    }
  }
  return "views://mainview/index.html";
}

connectToDaemon();

const url = await getMainViewUrl();

// Use 800x600 for resize testing; normal mode uses saved values
const RESIZE_TEST_ACTIVE = true; // HARDCODED — revert after testing
const savedX      = RESIZE_TEST_ACTIVE ? 200 : Number(settingsDb.getSetting("win_x")      ?? 100);
const savedY      = RESIZE_TEST_ACTIVE ? 200 : Number(settingsDb.getSetting("win_y")      ?? 100);
const savedWidth  = RESIZE_TEST_ACTIVE ? 800 : Number(settingsDb.getSetting("win_width")  ?? 1400);
const savedHeight = RESIZE_TEST_ACTIVE ? 600 : Number(settingsDb.getSetting("win_height") ?? 900);

mainWindow = new BrowserWindow({
  title: "Agent Orchestrator",
  titleBarStyle: "hidden",
  url,
  rpc,
  frame: {
    width:  isNaN(savedWidth)  ? 1400 : savedWidth,
    height: isNaN(savedHeight) ? 900  : savedHeight,
    x:      isNaN(savedX)      ? 100  : savedX,
    y:      isNaN(savedY)      ? 100  : savedY,
  },
});

// Native resize via C shared library — deferred 3s for GTK widget realization
setTimeout(() => {
  try {
    const { initNativeResize } = require("./native-resize.ts");
    initNativeResize((mainWindow as any).ptr, 8);
  } catch (e) { console.error("[native-resize] init failed:", e); }
}, 3000);

// Prevent GTK's false Ctrl+click detection from closing the window on initial load.
// WebKitGTK reports stale modifier state (0x14 = Ctrl+Alt) after SIGTERM of previous instance,
// which Electrobun interprets as a Cmd+click → "open in new window" → closes the main window.
// Fix: intercept new-window-open globally and suppress it.
try {
  // @ts-ignore — electrobunEventEmitter is an internal export
  const mod = await import("electrobun/bun");
  const emitter = (mod as any).electrobunEventEmitter ?? (mod as any).default?.electrobunEventEmitter;
  if (emitter?.on) {
    emitter.on("new-window-open", (event: any) => {
      console.log(`[new-window-open] Blocked false Ctrl+click: ${JSON.stringify(event?.detail ?? event)}`);
      if (event?.preventDefault) event.preventDefault();
    });
    console.log("[new-window-open] Handler registered successfully");
  } else {
    console.warn("[new-window-open] Could not find event emitter in electrobun/bun exports");
  }
} catch (err) {
  console.warn("[new-window-open] Could not register handler:", err);
}

console.log("Agent Orchestrator (Electrobun) started!");
