'use client';

import { useEffect, useRef, useState } from 'react';
import { Terminal } from '@xterm/xterm';
import { FitAddon } from '@xterm/addon-fit';
import '@xterm/xterm/css/xterm.css';

const SESSION_NAME = 'mw-forge-help';

function getWsUrl() {
  if (typeof window === 'undefined') return `ws://localhost:${parseInt(process.env.TERMINAL_PORT || '8404')}`;
  const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
  const wsHost = window.location.hostname;
  if (wsHost !== 'localhost' && wsHost !== '127.0.0.1') {
    return `${wsProtocol}//${window.location.host}/terminal-ws`;
  }
  const webPort = parseInt(window.location.port) || 8403;
  return `${wsProtocol}//${wsHost}:${webPort + 1}`;
}

export default function HelpTerminal() {
  const containerRef = useRef<HTMLDivElement>(null);
  const [connected, setConnected] = useState(false);

  useEffect(() => {
    if (!containerRef.current) return;

    let disposed = false;
    let dataDir = '~/.forge/data';
    // Full launch command for the Help AI: resolved from the DEFAULT agent's
    // `help` scene (binary path + --model models.help + env exports). Falls back
    // to bare `claude` if resolution fails.
    let launchCmd = 'claude';

    const cs = getComputedStyle(document.documentElement);
    const tv = (name: string) => cs.getPropertyValue(name).trim();
    const term = new Terminal({
      cursorBlink: true,
      fontSize: 12,
      fontFamily: 'Menlo, Monaco, "Courier New", monospace',
      scrollback: 3000,
      logger: { trace: () => {}, debug: () => {}, info: () => {}, warn: () => {}, error: () => {} },
      theme: {
        background: tv('--term-bg') || '#1a1a2e',
        foreground: tv('--term-fg') || '#e0e0e0',
        cursor: tv('--term-cursor') || '#7c5bf0',
        selectionBackground: (tv('--term-cursor') || '#7c5bf0') + '44',
      },
    });
    const fit = new FitAddon();
    term.loadAddon(fit);
    term.open(containerRef.current);
    try { fit.fit(); } catch {}

    const wsUrl = getWsUrl();
    let ws: WebSocket | null = null;
    let reconnectTimer = 0;
    let isNewSession = false;

    function connect() {
      if (disposed) return;
      const socket = new WebSocket(wsUrl);
      ws = socket;

      socket.onopen = () => {
        if (disposed) { socket.close(); return; }
        socket.send(JSON.stringify({ type: 'attach', sessionName: SESSION_NAME, cols: term.cols, rows: term.rows }));
      };

      socket.onmessage = (event) => {
        if (disposed) return;
        try {
          const msg = JSON.parse(event.data);
          if (msg.type === 'output') {
            try { term.write(msg.data); } catch {}
          } else if (msg.type === 'connected') {
            setConnected(true);
            if (isNewSession) {
              isNewSession = false;
              setTimeout(() => {
                if (socket.readyState === WebSocket.OPEN) {
                  socket.send(JSON.stringify({ type: 'input', data: `cd "${dataDir}" 2>/dev/null && ${launchCmd}\n` }));
                }
              }, 300);
            }
          } else if (msg.type === 'error') {
            isNewSession = true;
            if (socket.readyState === WebSocket.OPEN) {
              socket.send(JSON.stringify({ type: 'create', cols: term.cols, rows: term.rows, sessionName: SESSION_NAME }));
            }
          }
        } catch {}
      };

      socket.onclose = () => {
        if (disposed) return;
        setConnected(false);
        reconnectTimer = window.setTimeout(connect, 3000);
      };
      socket.onerror = () => {};
    }

    // Fetch data dir + default agent, then resolve that agent's `help`-scene
    // launch info (path + model + env) so Help runs the configured binary/model.
    Promise.all([
      fetch('/api/help?action=status').then(r => r.json()).then(data => { if (data.dataDir) dataDir = data.dataDir; }).catch(() => {}),
      fetch('/api/agents').then(r => r.json()).then(async (data) => {
        const defaultId = data.defaultAgent || 'claude';
        try {
          const info = await fetch(`/api/agents?resolve=${encodeURIComponent(defaultId)}&scene=help`).then(r => r.json());
          const bin = info?.cliCmd || 'claude';
          const modelFlag = info?.model ? ` --model ${info.model}` : '';
          const envPrefix = info?.env
            ? Object.entries(info.env as Record<string, string>)
                .map(([k, v]) => `export ${k}=${JSON.stringify(v)}`)
                .join(' && ') + ' && '
            : '';
          launchCmd = `${envPrefix}${bin}${modelFlag}`;
        } catch {
          // Fallback to bare path from the agent list if resolve fails.
          const agent = (data.agents || []).find((a: any) => a.id === defaultId);
          if (agent?.path) launchCmd = agent.path;
        }
      }).catch(() => {}),
    ]).finally(() => { if (!disposed) connect(); });

    term.onData((data) => {
      if (ws?.readyState === WebSocket.OPEN) ws.send(JSON.stringify({ type: 'input', data }));
    });

    const resizeObserver = new ResizeObserver(() => {
      const el = containerRef.current;
      if (!el || el.offsetWidth < 50 || el.offsetHeight < 30) return;
      try {
        fit.fit();
        if (term.cols < 2 || term.rows < 2) return;
        if (ws?.readyState === WebSocket.OPEN) {
          ws.send(JSON.stringify({ type: 'resize', cols: term.cols, rows: term.rows }));
        }
      } catch {}
    });
    resizeObserver.observe(containerRef.current);

    return () => {
      disposed = true;
      clearTimeout(reconnectTimer);
      ws?.close();
      resizeObserver.disconnect();
      term.dispose();
    };
  }, []);

  return (
    <div className="h-full flex flex-col">
      <div ref={containerRef} className="flex-1" />
    </div>
  );
}
