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

/** Configuration for the web search tool. */
export interface WebSearchConfig {
  provider: 'brave' | 'searxng';
  /** Required for Brave Search API. */
  apiKey?: string;
  /** Required for SearXNG (e.g. 'http://searxng:8080'). */
  endpoint?: string;
  /** Maximum number of results to return (default: 5). */
  maxResults?: number;
}

interface WebSearchArgs {
  query: string;
  count?: number;
}

interface SearchResult {
  title: string;
  url: string;
  snippet: string;
}

/** Fetch timeout in milliseconds. */
const FETCH_TIMEOUT_MS = 10_000;

/** Maximum allowed result count. */
const MAX_RESULTS = 20;

/** Default result count. */
const DEFAULT_RESULTS = 5;

/**
 * Format search results as numbered markdown.
 */
function formatResults(results: SearchResult[]): string {
  return results
    .map(
      (r, i) => `${i + 1}. **${r.title}** \u2014 ${r.url}\n   ${r.snippet}`,
    )
    .join('\n\n');
}

/**
 * Search using the Brave Search API.
 */
async function searchBrave(
  query: string,
  count: number,
  apiKey: string,
): Promise<SearchResult[]> {
  const params = new URLSearchParams({
    q: query,
    count: String(count),
  });

  const response = await fetch(
    `https://api.search.brave.com/res/v1/web/search?${params.toString()}`,
    {
      signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
      headers: {
        'Accept': 'application/json',
        'X-Subscription-Token': apiKey,
      },
    },
  );

  if (!response.ok) {
    const body = await response.text();
    throw new Error(`Brave API HTTP ${response.status}: ${body}`);
  }

  const data = (await response.json()) as {
    web?: { results?: Array<{ title: string; url: string; description: string }> };
  };

  const rawResults = data.web?.results ?? [];
  return rawResults.map((r) => ({
    title: r.title,
    url: r.url,
    snippet: r.description,
  }));
}

/**
 * Search using a self-hosted SearXNG instance.
 */
async function searchSearxng(
  query: string,
  count: number,
  endpoint: string,
): Promise<SearchResult[]> {
  // Strip trailing slash from endpoint
  const baseUrl = endpoint.replace(/\/+$/, '');
  const params = new URLSearchParams({
    q: query,
    format: 'json',
    categories: 'general',
  });

  const response = await fetch(`${baseUrl}/search?${params.toString()}`, {
    signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
    headers: {
      'Accept': 'application/json',
    },
  });

  if (!response.ok) {
    const body = await response.text();
    throw new Error(`SearXNG HTTP ${response.status}: ${body}`);
  }

  const data = (await response.json()) as {
    results?: Array<{ title: string; url: string; content: string }>;
  };

  const rawResults = (data.results ?? []).slice(0, count);
  return rawResults.map((r) => ({
    title: r.title,
    url: r.url,
    snippet: r.content,
  }));
}

/**
 * Creates a web.search tool configured for the given search provider.
 *
 * Supports Brave Search API and self-hosted SearXNG.
 */
export function createWebSearchTool(config: WebSearchConfig): Tool {
  const defaultCount = config.maxResults ?? DEFAULT_RESULTS;

  return {
    name: 'web.search',
    description:
      'Search the web for current information. Returns titles, URLs, and snippets from web search results. Use this to find up-to-date information about any topic.',
    requiredSecretScopes: ['web_search'],
    inputSchema: {
      type: 'object',
      properties: {
        query: { type: 'string', description: 'Search query' },
        count: {
          type: 'number',
          description: 'Number of results to return (default 5, max 20)',
        },
      },
      required: ['query'],
    },
    execute: async (rawArgs: unknown): Promise<ToolResult> => {
      const args = rawArgs as WebSearchArgs;
      // Clamp count: use provided value (capped at MAX_RESULTS), or fall back to default
      const count = Math.min(args.count ?? defaultCount, MAX_RESULTS);

      try {
        let results: SearchResult[];

        if (config.provider === 'brave') {
          if (!config.apiKey) {
            return {
              success: false,
              output: '',
              error: 'Brave Search API key not configured',
            };
          }
          results = await searchBrave(args.query, count, config.apiKey);
        } else {
          // SearXNG provider
          if (!config.endpoint) {
            return {
              success: false,
              output: '',
              error: 'SearXNG endpoint not configured',
            };
          }
          results = await searchSearxng(args.query, count, config.endpoint);
        }

        if (results.length === 0) {
          return {
            success: true,
            output: `No results found for: ${args.query}`,
          };
        }

        return { success: true, output: formatResults(results) };
      } catch (error) {
        return {
          success: false,
          output: '',
          error: error instanceof Error ? error.message : String(error),
        };
      }
    },
  };
}
