// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import * as Host from '../../../core/host/host.js';
import * as i18n from '../../../core/i18n/i18n.js';
import type * as SDK from '../../../core/sdk/sdk.js';
import * as Logs from '../../logs/logs.js';
import type {FunctionCallHandlerResult} from '../agents/AiAgent.js';
import {isOpaqueOrigin} from '../AiOrigins.js';
import {getRequestContextOrigin} from '../contexts/RequestContext.js';

import {
  type BaseToolCapability,
  type OriginLockCapability,
  type Tool,
  ToolName,
} from './Tool.js';

const UIStringsNotTranslate = {
  listingNetworkRequests: 'Listing network requests',
} as const;

const lockedString = i18n.i18n.lockedString;

interface NetworkRequestSummary {
  id: string;
  url: string;
  statusCode: number;
  duration: string;
  transferSize: string;
}

/**
 * A tool that lists all network requests recorded by DevTools.
 * Filters the list by the conversation's established origin to prevent cross-origin data exposure.
 */
export class ListNetworkRequestsTool implements
    Tool<Record<string, never>, unknown, BaseToolCapability&OriginLockCapability> {
  readonly name = ToolName.LIST_NETWORK_REQUESTS;
  readonly description = 'Gives a list of network requests including URL, status code, and duration.';

  readonly parameters: Host.AidaClient.FunctionObjectParam<never> = {
    type: Host.AidaClient.ParametersTypes.OBJECT,
    description: '',
    nullable: true,
    required: [],
    properties: {},
  };

  displayInfoFromArgs(): {
    title: string,
    action: string,
  } {
    return {
      title: lockedString(UIStringsNotTranslate.listingNetworkRequests),
      action: 'listNetworkRequests()',
    };
  }

  /**
   * Handles the request to list network requests.
   * Returns requests matching the conversation's established origin, if set.
   */
  async handler(
      _params: Record<string, never>,
      context: BaseToolCapability&OriginLockCapability,
      ): Promise<FunctionCallHandlerResult<unknown>> {
    const requests: NetworkRequestSummary[] = [];
    // A conversation is locked to an origin once the first query is made.
    // We only allow inspecting requests matching the conversation's established origin.
    const origin = context.getEstablishedOrigin();

    // Opaque origins are never allowed to be used as context.
    if (origin && isOpaqueOrigin(origin)) {
      return {
        error: 'Opaque origin not allowed',
      };
    }

    let hasCrossOriginRequest = false;
    const requestsToShow: SDK.NetworkRequest.NetworkRequest[] = [];
    for (const request of Logs.NetworkLog.NetworkLog.instance().requests()) {
      // To prevent cross-origin prompt injection attacks, HAR-imported requests
      // are assigned a virtual origin (e.g., `imported-har://${domain}`) rather than
      // sharing the origin of live pages.
      const requestOrigin = getRequestContextOrigin(request);

      // If the conversation is locked to an origin, skip requests from other origins.
      if (origin && requestOrigin !== origin) {
        hasCrossOriginRequest = true;
        continue;
      }

      requests.push({
        id: request.requestId(),
        url: request.url(),
        statusCode: request.statusCode,
        duration: i18n.TimeUtilities.secondsToString(request.duration),
        transferSize: i18n.ByteUtilities.formatBytesToKb(request.transferSize),
      });
      requestsToShow.push(request);
    }

    if (requests.length === 0) {
      return {
        // If there were requests but they were filtered out due to the origin lock,
        // we ask the user to start a new chat so they can select a request from the other origin.
        error: hasCrossOriginRequest ? `No requests showing with origin ${origin}. Tell the user to start a new chat` :
                                       'No requests recorded by DevTools',
      };
    }

    return {
      result: JSON.stringify(requests),
      widgets: [{
        name: 'NETWORK_REQUESTS_LIST',
        data: {
          requests: requestsToShow,
        },
      }],
    };
  }
}
