/**
 * CDP connection wrapper — typed, ergonomic API over chrome-remote-interface.
 */

import CDP from 'chrome-remote-interface';
import { config } from './config.js';

type Domains = CDP.Client;

export interface BrowserContext {
  client: CDP.Client;
  Network: Domains['Network'];
  Page: Domains['Page'];
  Runtime: Domains['Runtime'];
  Input: Domains['Input'];
  DOM: Domains['DOM'];
  evaluate<T>(expression: string): Promise<T>;
  querySelector(selector: string): Promise<number | null>;
  getBoxModel(nodeId: number): Promise<{ x: number; y: number; width: number; height: number } | null>;
  close(): Promise<void>;
}

/**
 * Connect to a Chromium instance via CDP.
 * @param port  Remote debugging port (default from config: 9222)
 */
export async function connect(port?: number): Promise<BrowserContext> {
  const targetPort = port ?? config.cdp.port;

  let client: CDP.Client;
  try {
    client = await CDP({ host: config.cdp.host, port: targetPort });
  } catch (err) {
    const msg =
      err instanceof Error ? err.message : String(err);
    throw new Error(
      `CDP connection failed on ${config.cdp.host}:${targetPort}: ${msg}\n` +
        'Is Chromium running with --remote-debugging-port=9222?',
    );
  }

  const { Network, Page, Runtime, Input, DOM } = client;

  // Enable required domains
  await Promise.all([
    Network.enable({}),
    Page.enable(),
    Runtime.enable(),
    DOM.enable(),
  ]);

  async function evaluate<T>(expression: string): Promise<T> {
    const result = await Runtime.evaluate({
      expression,
      returnByValue: true,
      awaitPromise: true,
    });
    if (result.exceptionDetails) {
      throw new Error(
        `evaluate failed: ${result.exceptionDetails.text ?? JSON.stringify(result.exceptionDetails)}`,
      );
    }
    return result.result.value as T;
  }

  async function querySelector(selector: string): Promise<number | null> {
    const doc = await DOM.getDocument({ depth: 0 });
    const { nodeId } = await DOM.querySelector({
      nodeId: doc.root.nodeId,
      selector,
    });
    return nodeId === 0 ? null : nodeId;
  }

  async function getBoxModel(
    nodeId: number,
  ): Promise<{ x: number; y: number; width: number; height: number } | null> {
    try {
      const { model } = await DOM.getBoxModel({ nodeId });
      // model.content is [x1,y1, x2,y2, x3,y3, x4,y4] (quad)
      const [x1, y1, x2, , , y3] = model.content;
      return {
        x: x1,
        y: y1,
        width: x2 - x1,
        height: y3 - y1,
      };
    } catch {
      return null;
    }
  }

  async function close(): Promise<void> {
    await client.close();
  }

  return {
    client,
    Network,
    Page,
    Runtime,
    Input,
    DOM,
    evaluate,
    querySelector,
    getBoxModel,
    close,
  };
}
