import type { AgentTool } from "@jaeswift/jae-agent-core";
import type { ToolResultMessage } from "@jaeswift/jae-ai";
import { Type } from "@sinclair/typebox";
import { html } from "lit";
import { Globe } from "lucide";
import { registerToolRenderer, renderHeader } from "./renderer-registry.js";
import type { ToolRenderer, ToolRenderResult } from "./types.js";

const TOOL_SERVER =
  typeof window !== "undefined"
    ? (window as any).__JAE_TOOL_SERVER__ || "http://localhost:7700"
    : "http://localhost:7700";

const browserSchema = Type.Object({
  action: Type.Union(
    [
      Type.Literal("navigate"),
      Type.Literal("click"),
      Type.Literal("type"),
      Type.Literal("scroll"),
      Type.Literal("back"),
      Type.Literal("screenshot"),
      Type.Literal("text"),
      Type.Literal("eval"),
      Type.Literal("elements"),
      Type.Literal("keypress"),
    ],
    { description: "Browser action to perform" },
  ),
  url: Type.Optional(Type.String({ description: "URL to navigate to" })),
  index: Type.Optional(Type.Number({ description: "Element index number from the elements list to click or type into" })),
  x: Type.Optional(Type.Number({ description: "Click X coordinate (use index instead when possible)" })),
  y: Type.Optional(Type.Number({ description: "Click Y coordinate (use index instead when possible)" })),
  text: Type.Optional(Type.String({ description: "Text to type into focused element or element by index" })),
  selector: Type.Optional(Type.String({ description: "CSS selector to type into (prefer index instead)" })),
  dy: Type.Optional(Type.Number({ description: "Scroll delta Y pixels (positive=down, negative=up)" })),
  script: Type.Optional(Type.String({ description: "JavaScript to evaluate in page" })),
  key: Type.Optional(Type.String({ description: "Key to press: Enter, Tab, Escape, Backspace, etc." })),
});

export interface BrowserDetails {
  action: string;
  url?: string;
  title?: string;
  screenshot?: string;
  text?: string;
  elementsText?: string;
  evalResult?: string;
  error?: string;
}

export const browserTool: AgentTool<typeof browserSchema, BrowserDetails> = {
  name: "browser",
  label: "Browser",
  description: `Control a browser to navigate, click, type, and read web pages.

WORKFLOW: Always follow this pattern:
1. navigate to a URL - returns page elements list
2. READ the numbered elements list to find what you need
3. click by index number OR type into an element by index
4. After each action you get an updated elements list

Actions:
- navigate: open a URL (provide "url")
- click: click element by "index" (preferred) or by "x","y" coordinates
- type: type "text" into element by "index", or into currently focused element
- keypress: press a key like "Enter", "Tab", "Escape" (provide "key")
- scroll: scroll page (provide "dy", positive=down negative=up)
- back: go back in history
- screenshot: get fresh screenshot and elements list
- elements: get just the interactive elements list
- text: get all visible text content of the page
- eval: run JavaScript on the page (provide "script")

EXAMPLE - Search Google:
1. browser({action:"navigate", url:"google.com"}) → see elements, find input[text] "Search" at index 5
2. browser({action:"type", index:5, text:"my search query"}) → typed into search box
3. browser({action:"keypress", key:"Enter"}) → submitted search
4. Read results from elements list, click a link by index`,
  parameters: browserSchema,
  async execute(toolCallId, params, signal) {
    const { action, ...rest } = params;
    const endpointMap: Record<string, string> = {
      navigate: "/api/browser/navigate",
      click: "/api/browser/click",
      type: "/api/browser/type",
      scroll: "/api/browser/scroll",
      back: "/api/browser/back",
      screenshot: "/api/browser/screenshot",
      text: "/api/browser/text",
      eval: "/api/browser/eval",
      elements: "/api/browser/elements",
      keypress: "/api/browser/keypress",
    };
    const endpoint = endpointMap[action];
    if (!endpoint) {
      return {
        content: [{ type: "text" as const, text: "Unknown action: " + action }],
        details: { action, error: "Unknown action" },
      };
    }
    try {
      const res = await fetch(TOOL_SERVER + endpoint, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(rest),
        signal,
      });
      const data = (await res.json()) as any;
      if (data.error) {
        return {
          content: [{ type: "text" as const, text: "Browser error: " + data.error }],
          details: { action, error: data.error },
        };
      }
      // Build text response for LLM — elements list is the key info
      const textParts: string[] = [];
      if (data.url) textParts.push("URL: " + data.url);
      if (data.title) textParts.push("Title: " + data.title);
      // Always include elements text when available — this is how the model "sees" the page
      if (data.elementsText) textParts.push("\n" + data.elementsText);
      if (data.text) textParts.push("\nPage text (truncated):\n" + data.text.slice(0, 3000));
      if (data.evalResult) textParts.push("Eval result: " + data.evalResult);
      if (data.screenshot) textParts.push("\n[Screenshot captured and displayed in browser panel]");
      if (textParts.length === 0) textParts.push("Action completed.");
      // Include screenshot as image content if available (for vision models)
      const content: any[] = [{ type: "text" as const, text: textParts.join("\n") }];
      if (data.screenshot) {
        content.push({ type: "image" as const, mimeType: "image/jpeg", data: data.screenshot });
      }
      return {
        content,
        details: {
          action,
          url: data.url,
          title: data.title,
          screenshot: data.screenshot,
          text: data.text,
          elementsText: data.elementsText,
          evalResult: data.evalResult,
        },
      };
    } catch (err: any) {
      return {
        content: [{ type: "text" as const, text: "Browser tool error: " + err.message }],
        details: { action, error: err.message },
      };
    }
  },
};

class BrowserToolRenderer implements ToolRenderer<any, BrowserDetails> {
  render(params: any | undefined, result: ToolResultMessage<BrowserDetails> | undefined): ToolRenderResult {
    const state = result ? (result.isError ? "error" : "complete") : "inprogress";
    const action = result?.details?.action || params?.action || "...";
    const url = result?.details?.url || params?.url || "";
    const label = url ? action + ": " + url : action;
    if (result?.details?.screenshot) {
      return {
        content: html`
          <div class="flex flex-col gap-2">
            ${renderHeader(state, Globe, label)}
            <img src="data:image/jpeg;base64,${result.details.screenshot}"
              class="rounded border border-border max-w-full" style="max-height:400px"
              alt="Browser screenshot" />
            ${result.details.title ? html`<span class="text-xs text-muted-foreground">${result.details.title}</span>` : html``}
          </div>`,
        isCustom: false,
      };
    }
    if (result?.details?.elementsText) {
      return {
        content: html`
          <div class="flex flex-col gap-2">
            ${renderHeader(state, Globe, label)}
            <pre class="text-xs p-3 rounded border border-border overflow-auto max-h-48 whitespace-pre-wrap">${result.details.elementsText}</pre>
          </div>`,
        isCustom: false,
      };
    }
    if (result?.details?.text) {
      return {
        content: html`
          <div class="flex flex-col gap-2">
            ${renderHeader(state, Globe, label)}
            <pre class="text-xs p-3 rounded border border-border overflow-auto max-h-48 whitespace-pre-wrap">${result.details.text}</pre>
          </div>`,
        isCustom: false,
      };
    }
    return { content: renderHeader(state, Globe, label), isCustom: false };
  }
}

registerToolRenderer("browser", new BrowserToolRenderer());

export function createBrowserTool(): AgentTool<typeof browserSchema, BrowserDetails> {
  return browserTool;
}
