import type { Tool, ToolResult } from '../../types.js';
import type { BrowserManager } from './manager.js';

/** Create all browser tools bound to a BrowserManager instance. */
export function createBrowserTools(manager: BrowserManager): Tool[] {
  return [
    createBrowserNavigateTool(manager),
    createBrowserScreenshotTool(manager),
    createBrowserClickTool(manager),
    createBrowserTypeTool(manager),
    createBrowserContentTool(manager),
    createBrowserEvalTool(manager),
  ];
}

function createBrowserNavigateTool(manager: BrowserManager): Tool {
  return {
    name: 'browser.navigate',
    description: 'Navigate to a URL in the browser. Returns the page title and URL after navigation.',
    inputSchema: {
      type: 'object',
      properties: {
        url: { type: 'string', description: 'The URL to navigate to' },
        waitUntil: {
          type: 'string',
          description: 'When to consider navigation complete: load, domcontentloaded, networkidle0, networkidle2 (default: domcontentloaded)',
        },
      },
      required: ['url'],
    },
    execute: async (rawArgs: unknown): Promise<ToolResult> => {
      const args = rawArgs as { url: string; waitUntil?: string };
      try {
        const page = await manager.getPage();
        const waitUntil = (args.waitUntil ?? 'domcontentloaded') as 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2';
        await page.goto(args.url, { waitUntil });
        const title = await page.title();
        const currentUrl = page.url();
        return {
          success: true,
          output: `Navigated to: ${currentUrl}\nTitle: ${title}`,
        };
      } catch (error) {
        return {
          success: false,
          output: '',
          error: error instanceof Error ? error.message : String(error),
        };
      }
    },
  };
}

function createBrowserScreenshotTool(manager: BrowserManager): Tool {
  return {
    name: 'browser.screenshot',
    description: 'Take a screenshot of the current page. Returns the screenshot as a base64-encoded PNG.',
    inputSchema: {
      type: 'object',
      properties: {
        fullPage: { type: 'boolean', description: 'Capture full scrollable page (default: false)' },
        selector: { type: 'string', description: 'CSS selector to screenshot a specific element' },
      },
    },
    execute: async (rawArgs: unknown): Promise<ToolResult> => {
      const args = rawArgs as { fullPage?: boolean; selector?: string };
      try {
        const page = await manager.getPage();

        let screenshotData: string;
        if (args.selector) {
          const element = await page.$(args.selector);
          if (!element) {
            return { success: false, output: '', error: `Element not found: ${args.selector}` };
          }
          screenshotData = (await element.screenshot({ encoding: 'base64' })) as string;
        } else {
          screenshotData = (await page.screenshot({
            encoding: 'base64',
            fullPage: args.fullPage ?? false,
          })) as string;
        }

        return {
          success: true,
          output: `Screenshot captured (base64 PNG, ${screenshotData.length} chars):\n${screenshotData.slice(0, 200)}...`,
        };
      } catch (error) {
        return {
          success: false,
          output: '',
          error: error instanceof Error ? error.message : String(error),
        };
      }
    },
  };
}

function createBrowserClickTool(manager: BrowserManager): Tool {
  return {
    name: 'browser.click',
    description: 'Click an element on the page identified by CSS selector.',
    inputSchema: {
      type: 'object',
      properties: {
        selector: { type: 'string', description: 'CSS selector of the element to click' },
      },
      required: ['selector'],
    },
    execute: async (rawArgs: unknown): Promise<ToolResult> => {
      const args = rawArgs as { selector: string };
      try {
        const page = await manager.getPage();
        await page.click(args.selector);
        return { success: true, output: `Clicked element: ${args.selector}` };
      } catch (error) {
        return {
          success: false,
          output: '',
          error: error instanceof Error ? error.message : String(error),
        };
      }
    },
  };
}

function createBrowserTypeTool(manager: BrowserManager): Tool {
  return {
    name: 'browser.type',
    description: 'Type text into an input element on the page.',
    inputSchema: {
      type: 'object',
      properties: {
        selector: { type: 'string', description: 'CSS selector of the input element' },
        text: { type: 'string', description: 'Text to type' },
        clear: { type: 'boolean', description: 'Clear the field before typing (default: false)' },
      },
      required: ['selector', 'text'],
    },
    execute: async (rawArgs: unknown): Promise<ToolResult> => {
      const args = rawArgs as { selector: string; text: string; clear?: boolean };
      try {
        const page = await manager.getPage();
        if (args.clear) {
          await page.click(args.selector, { count: 3 }); // Select all
          await page.keyboard.press('Backspace');
        }
        await page.type(args.selector, args.text);
        return { success: true, output: `Typed "${args.text}" into ${args.selector}` };
      } catch (error) {
        return {
          success: false,
          output: '',
          error: error instanceof Error ? error.message : String(error),
        };
      }
    },
  };
}

function createBrowserContentTool(manager: BrowserManager): Tool {
  return {
    name: 'browser.content',
    description: 'Get the text content of the page or a specific element. Returns extracted text (not raw HTML).',
    inputSchema: {
      type: 'object',
      properties: {
        selector: { type: 'string', description: 'CSS selector to get content of a specific element (default: body)' },
        maxLength: { type: 'number', description: 'Maximum characters to return (default: 10000)' },
      },
    },
    execute: async (rawArgs: unknown): Promise<ToolResult> => {
      const args = rawArgs as { selector?: string; maxLength?: number };
      try {
        const page = await manager.getPage();
        const selector = args.selector ?? 'body';
        const maxLength = args.maxLength ?? 10000;

        // The $eval callback executes in the browser context where DOM types exist,
        // but TS checks against Node.js lib (no DOM). Use `any` to bridge the gap.
        const text = await page.$eval(selector, (el) => {
          const htmlEl = el as unknown as { innerText?: string; textContent?: string | null };
          return htmlEl.innerText || htmlEl.textContent || '';
        });

        const truncated = text.length > maxLength
          ? text.slice(0, maxLength) + `\n... (truncated, ${text.length} total chars)`
          : text;

        const title = await page.title();
        const url = page.url();

        return {
          success: true,
          output: `URL: ${url}\nTitle: ${title}\n\n${truncated}`,
        };
      } catch (error) {
        return {
          success: false,
          output: '',
          error: error instanceof Error ? error.message : String(error),
        };
      }
    },
  };
}

function createBrowserEvalTool(manager: BrowserManager): Tool {
  return {
    name: 'browser.eval',
    description: 'Evaluate JavaScript in the browser page context. Returns the result as a string.',
    inputSchema: {
      type: 'object',
      properties: {
        expression: { type: 'string', description: 'JavaScript expression to evaluate in the page context' },
      },
      required: ['expression'],
    },
    execute: async (rawArgs: unknown): Promise<ToolResult> => {
      const args = rawArgs as { expression: string };
      try {
        const page = await manager.getPage();
        // Use evaluate with a function that evaluates the expression string
        const result = await page.evaluate((expr: string) => {
          // eslint-disable-next-line no-eval
          return eval(expr);
        }, args.expression);
        const output = typeof result === 'string' ? result : JSON.stringify(result, null, 2);
        return { success: true, output: output ?? 'undefined' };
      } catch (error) {
        return {
          success: false,
          output: '',
          error: error instanceof Error ? error.message : String(error),
        };
      }
    },
  };
}
