/**
 * web_browser — 统一浏览器交互工具
 *
 * 将 9 个浏览器操作合并为单个工具，通过 `action` 参数路由：
 *   1. open       — 打开 URL
 *   2. screenshot — 截图（视口/元素/全页三模式）
 *   3. evaluate   — 执行 JavaScript
 *   4. extract    — 提取页面文本与链接
 *   5. click      — 点击元素
 *   6. type       — 输入文本
 *   7. scroll     — 滚动页面
 *   8. wait       — 等待条件
 *   9. close      — 关闭窗口
 *
 * 基于单例 BrowserWindowManager 实现。
 *
 * @see docs/Agent网络工具通用设计-v2.md — 第 4 章 browser 浏览器设计
 */

import type { IMetonaTool, ToolExecutionContext } from '../../types/metona-tool';
import type { MetonaToolDef } from '../../../harness/types';
import { MetonaToolCategory, MetonaRiskLevel } from '../../../harness/types';
import { BrowserWindowManager } from './browser-window-manager';
import { logTool } from './network-utils';

// ===== 单例 Manager =====

let managerInstance: BrowserWindowManager | null = null;

function getManager(): BrowserWindowManager {
  if (!managerInstance) {
    managerInstance = new BrowserWindowManager();
  }
  return managerInstance;
}

/** 获取共享浏览器管理器单例（供 web_fetch 等工具复用） */
export function getBrowserManager(): BrowserWindowManager {
  return getManager();
}

/** 应用退出时清理（由 main.ts 调用） */
export function cleanupBrowser(): void {
  BrowserWindowManager.cleanup(managerInstance);
  managerInstance = null;
}

// ===== WebBrowserTool =====

export class WebBrowserTool implements IMetonaTool {
  readonly definition: MetonaToolDef = {
    name: 'web_browser',
    description: [
      'Control a hidden browser window to navigate, inspect, and interact with web pages.',
      'Use the "action" parameter to specify the operation:',
      '  open       — Load a URL; optionally wait for a CSS selector before returning. Returns page title and URL.',
      '  screenshot — Capture the viewport (default), full page, or a specific element by CSS selector. Returns base64 PNG.',
      '  evaluate   — Execute JavaScript in the page context. Returns the result of the last expression.',
      '  extract    — Extract clean text and up to 50 links from the page or a specific element.',
      '  click      — Click an element by CSS selector. Scrolls into view first. Can optionally wait for the selector.',
      '  type       — Type text into an input element. Optionally clear first and submit the form.',
      '  scroll     — Scroll the page: down/up (500px), top/bottom, or scroll to an element by selector.',
      '  wait       — Wait for a CSS selector to appear (with timeout) or a fixed duration.',
      '  close      — Close the browser window and release resources.',
    ].join('\n'),
    parameters: {
      type: 'object',
      properties: {
        action: {
          type: 'string',
          enum: ['open', 'screenshot', 'evaluate', 'extract', 'click', 'type', 'scroll', 'wait', 'close'],
          description: 'Browser operation to perform',
        },
        url: {
          type: 'string',
          description: '[open] Target URL (http/https only)',
        },
        wait_selector: {
          type: 'string',
          description: '[open] CSS selector to wait for before returning (optional)',
        },
        script: {
          type: 'string',
          description: '[evaluate] JavaScript code to execute (must return a value)',
        },
        selector: {
          type: 'string',
          description: '[screenshot|extract|click|type|scroll] CSS selector of the target element',
        },
        full_page: {
          type: 'boolean',
          description: '[screenshot] Capture entire scrollable page (default false)',
        },
        text: {
          type: 'string',
          description: '[type] Text to type into the element',
        },
        clear: {
          type: 'boolean',
          description: '[type] Clear the field before typing (default true)',
        },
        submit: {
          type: 'boolean',
          description: '[type] Submit the form after typing (default false)',
        },
        direction: {
          type: 'string',
          enum: ['down', 'up', 'top', 'bottom'],
          description: '[scroll] Scroll direction (default down). Ignored if selector is provided.',
        },
        wait: {
          type: 'boolean',
          description: '[click] Wait for selector to appear before clicking (default false)',
        },
        time_ms: {
          type: 'number',
          description: '[wait] Fixed wait time in milliseconds (default 1000). Used as timeout when selector is also provided.',
        },
      },
      required: ['action'],
    },
    category: MetonaToolCategory.NETWORK,
    riskLevel: MetonaRiskLevel.HIGH,
    requiresPermission: true,
    timeoutMs: 60_000,
  };

  async execute(args: Record<string, unknown>, _context: ToolExecutionContext): Promise<unknown> {
    const action = args.action as string;
    if (!action) {
      return { success: false, error: 'Missing required parameter: action' };
    }

    logTool('web_browser', `action=${action}`);

    switch (action) {
      // ===== open =====
      case 'open': {
        const url = args.url as string;
        if (!url || !/^https?:\/\//i.test(url)) {
          return { success: false, error: 'URL must start with http:// or https://' };
        }
        const waitSelector = args.wait_selector as string | undefined;
        try {
          const result = await getManager().open({ url, waitSelector });
          return { success: true, action, ...result };
        } catch (err) {
          return { success: false, action, error: (err as Error).message };
        }
      }

      // ===== screenshot =====
      case 'screenshot': {
        const fullPage = (args.full_page as boolean) ?? false;
        const selector = args.selector as string | undefined;
        try {
          const result = await getManager().screenshot({ fullPage, selector });
          return {
            success: true,
            action,
            image: result.data,
            width: result.width,
            height: result.height,
            mime_type: 'image/png',
          };
        } catch (err) {
          return { success: false, action, error: (err as Error).message };
        }
      }

      // ===== evaluate =====
      case 'evaluate': {
        const script = args.script as string;
        if (!script) {
          return { success: false, action, error: 'No script provided' };
        }
        try {
          const result = await getManager().evaluate(script);
          return { success: true, action, result };
        } catch (err) {
          return { success: false, action, error: (err as Error).message };
        }
      }

      // ===== extract =====
      case 'extract': {
        const selector = args.selector as string | undefined;
        try {
          const result = await getManager().extract(selector);
          return {
            success: true,
            action,
            text: result.text,
            links: result.links,
            link_count: result.links.length,
          };
        } catch (err) {
          return { success: false, action, error: (err as Error).message };
        }
      }

      // ===== click =====
      case 'click': {
        const selector = args.selector as string;
        const wait = (args.wait as boolean) ?? false;
        if (!selector) {
          return { success: false, action, error: 'No selector provided' };
        }
        try {
          await getManager().click(selector, wait);
          return { success: true, action, selector, clicked: true };
        } catch (err) {
          return { success: false, action, error: (err as Error).message };
        }
      }

      // ===== type =====
      case 'type': {
        const selector = args.selector as string;
        const text = args.text as string;
        const clear = (args.clear as boolean) ?? true;
        const submit = (args.submit as boolean) ?? false;
        if (!selector) {
          return { success: false, action, error: 'No selector provided' };
        }
        if (text === undefined || text === null) {
          return { success: false, action, error: 'No text provided' };
        }
        try {
          await getManager().type(selector, text, { clear, submit });
          return { success: true, action, selector, typed: text.length };
        } catch (err) {
          return { success: false, action, error: (err as Error).message };
        }
      }

      // ===== scroll =====
      case 'scroll': {
        const direction = (args.direction as string) ?? 'down';
        const selector = args.selector as string | undefined;
        try {
          await getManager().scroll({
            direction: direction as 'down' | 'up' | 'top' | 'bottom',
            selector,
          });
          return { success: true, action, direction, selector };
        } catch (err) {
          return { success: false, action, error: (err as Error).message };
        }
      }

      // ===== wait =====
      case 'wait': {
        const selector = args.selector as string | undefined;
        const timeMs = (args.time_ms as number) ?? 1_000;
        try {
          await getManager().wait({ selector, timeMs });
          return { success: true, action, waited_for: selector ?? `${timeMs}ms` };
        } catch (err) {
          return { success: false, action, error: (err as Error).message };
        }
      }

      // ===== close =====
      case 'close': {
        getManager().close();
        return { success: true, action, closed: true };
      }

      default:
        return { success: false, error: `Unknown action: ${action}` };
    }
  }
}
