export class AgentParticipant {
  /**
   * This represents unique ID of the agentParticipant who joined the meeting.
   */
  id: string;

  /**
   * This represents display name of the agentParticipant.
   */
  displayName: string;

  /**
   * This represents all media streams associated with the agentParticipant. Streams could be `audio` or `video`.
   */
  streams: Map<string, Stream>;

  /**
   * This represents whether the participant is an agent.
   */
  isAgent: boolean;

  /**
   * This represents the agent ID of the agentParticipant.
   */
  agentId: string;

  /**
   * This represents the current pin state of the agentParticipant.
   */
  pinState: {
    cam: boolean;
    share: boolean;
  };

  /**
   * This indicates whether the agentParticipant’s webcam is currently enabled.
   */
  webcamOn: boolean;

  /**
   * This indicates whether the agentParticipant’s microphone is currently enabled.
   */
  micOn: boolean;

  /**
   * This represents the agentParticipant’s current mode.
   */
  mode: "SEND_AND_RECV" | "SIGNALLING_ONLY" | "RECV_ONLY";

  /**
   * This represents agentParticipant’s metadata provided while initializing the meeting
   */
  metaData: object;

  /**
   * - This method can be used to removes the remote agentParticipant from the meeting.
   *
   * @example
   * ```ts
   * let meeting;
   *
   * // Initialize Meeting
   * meeting = VideoSDK.initMeeting({
   *   // ...
   * });
   *
   * const agentParticipant = Array.from(meeting.participants.values()).find(
   *   (p) => p.isAgent
   * );
   *
   * agentParticipant.remove();
   * ```
   */
  remove(): void;

  /**
   * - This method can be used to pin the agentParticipant’s camera, screen share, or both.
   *
   * - Every participant receives a {@link MeetingEvent.pin-state-changed | pin-state-changed} event when the pin state is updated.
   *
   * @example
   * ```ts
   * let meeting;
   *
   * // Initialize Meeting
   * meeting = VideoSDK.initMeeting({
   *   // ...
   * });
   *
   * const agentParticipant = Array.from(meeting.participants.values()).find(
   *   (p) => p.isAgent
   * );
   *
   * agentParticipant.pin("CAM");
   * ```
   */
  pin(
    /**
     * Specifies which stream to pin.
     *
     * **Allowed values:**
     * - `"SHARE_AND_CAM"` – Pins both screen share and camera streams.
     * - `"CAM"` – Pins only the camera stream.
     * - `"SHARE"` – Pins only the screen-share stream.
     * @default SHARE_AND_CAM
     */
    type: "SHARE_AND_CAM" | "CAM" | "SHARE",
  ): void;

  /**
   * - This method can be used to unpin the agentParticipant’s camera, screen share, or both.
   *
   * - Every participant receives a {@link MeetingEvent.pin-state-changed | pin-state-changed} event when the pin state is updated.
   *
   * @example
   * ```ts
   * let meeting;
   *
   * // Initialize Meeting
   * meeting = VideoSDK.initMeeting({
   *   // ...
   * });
   *
   * const agentParticipant = Array.from(meeting.participants.values()).find(
   *   (p) => p.isAgent
   * );
   *
   * agentParticipant.unpin("CAM");
   * ```
   */
  unpin(
    /**
     *  Specifies which stream to unpin.
     *
     * **Allowed values:**
     * - `"SHARE_AND_CAM"` – Unpins both screen share and camera streams.
     * - `"CAM"` – Unpins only the camera stream.
     * - `"SHARE"` – Unpins only the screen-share stream.
     *
     * @default "SHARE_AND_CAM"
     */
    type: "SHARE_AND_CAM" | "CAM" | "SHARE",
  ): void;

  /**
   * - This method creates and returns a `<div>` element that internally manages the rendering of the agentParticipant’s video stream.
   * - It allows you to control the rendered stream type, quality preference and styling of both the video element and its container.
   *
   * @param options Optional configuration for rendering the stream.
   *
   * @param options.type
   * Specifies which stream to render.
   *
   * **Allowed value:**
   * - `"video"` – Camera video stream
   *
   * @param options.maxQuality
   * Sets the preferred maximum quality for the rendered stream.
   *
   * **Allowed value:**
   * - `"auto"` – Automatically adapts quality based on network conditions (default)
   * - `"high"` – Highest available quality
   * - `"med"` – Medium quality
   * - `"low"` – Low quality for bandwidth-constrained scenarios
   *
   * @param options.videostyle
   * CSS styles applied directly to the internal `<video>` element.
   *
   * @param options.containerStyle
   * CSS styles applied to the outer container `<div>`.
   *
   * @returns
   * An `HTMLDivElement` containing the rendered video or screen-share stream.
   *
   * @example
   * ```ts
   * const videoElement = agentParticipant.renderVideo({
   *   type: "video",
   *   maxQuality: "high",
   *   videostyle: {
   *     objectFit: "cover",
   *     borderRadius: "8px",
   *   },
   *   containerStyle: {
   *     width: "300px",
   *     height: "200px",
   *   },
   * });
   *
   * document.body.appendChild(videoElement);
   * ```
   */
  renderVideo(options?: {
    type?: "video";
    maxQuality?: "auto" | "high" | "med" | "low";
    videostyle?: Partial<CSSStyleDeclaration>;
    containerStyle?: Partial<CSSStyleDeclaration>;
  }): HTMLDivElement;

  /**
   * - This method creates and returns an `HTMLAudioElement` that can be used to play incoming audio streams in the DOM.
   *
   * @param options
   *
   * @returns
   * An `HTMLAudioElement` that plays the requested audio stream.
   *
   * @example
   * ```ts
   * const audioElement = agentParticipant.renderAudio({
   *   type: "audio",
   * });
   *
   * document.body.appendChild(audioElement);
   * ```
   */
  renderAudio(options?: {
    /**
     * **Type:** `"audio"
     *
     * - `"audio"` – Renders the agentParticipant's microphone audio stream.
     *
     */
    type?: "audio";
  }): HTMLAudioElement;

  /**
   * - This method can be used to get detailed statistics about the agentParticipant's video stream.
   * - These metrics provide insights into stream quality, network conditions, and transmission performance at the time the method is called.
   *
   * @returns
   * An array of objects containing the following metrics:
   *
   * - `jitter` – Represents variation in packet arrival time (stream instability).
   * - `bitrate` – The bitrate at which the video stream is being transmitted.
   * - `totalPackets` – Total number of packets transmitted for the stream.
   * - `packetsLost` – Total number of packets lost during transmission.
   * - `rtt` – Round-trip time (in milliseconds) between the client and server.
   * - `codec` – Codec used for encoding the video stream.
   * - `network` – Network type used for transmitting the stream.
   * - `limitation` – Indicates any limitations affecting stream quality.
   * - `size` – Resolution or size information related to the stream.
   *
   * > **Info**
   * >
   * > If the `rtt` value exceeds 300ms, consider switching to a region closer to the user for improved performance. Learn more [visit here](https://docs.videosdk.live/api-reference/realtime-communication/create-room).
   *
   * @example
   * ```ts
   * let meeting;
   *
   * // Initialize Meeting
   * meeting = VideoSDK.initMeeting({
   *   // ...
   * });
   *
   * const agentParticipant = Array.from(meeting.participants.values()).find(
   *   (p) => p.isAgent
   * );
   *
   * const videoStats = await agentParticipant.getVideoStats();
   * ```
   */
  getVideoStats(): Promise<
    Array<{
      bitrate: number;
      rtt: number;
      network: string;
      codec: string;
      jitter: number;
      limitation: any;
      totalPackets: number;
      packetsLost: number;
      size: any;
      currentSpatialLayer: number;
      currentTemporalLayer: number;
      preferredSpatialLayer: number;
      preferredTemporalLayer: number;
    }>
  >;

  /**
   * - This method can be used to get detailed statistics about the agentParticipant's audio stream.
   * - These metrics provide insights into stream quality, network conditions, and transmission performance at the time the method is called.
   *
   * @returns
   * An array of objects containing the following metrics:
   *
   * - `jitter` – Represents variation in packet arrival time (stream instability).
   * - `bitrate` – The bitrate at which the audio stream is being transmitted.
   * - `totalPackets` – Total number of packets transmitted for the stream.
   * - `packetsLost` – Total number of packets lost during transmission.
   * - `rtt` – Round-trip time (in milliseconds) between the client and server.
   * - `codec` – Codec used for encoding the audio stream.
   * - `network` – Network type used for transmitting the stream.
   *
   * > **Info**
   * >
   * > If the `rtt` value exceeds 300ms, consider switching to a region closer to the user for improved performance. Learn more [visit here](https://docs.videosdk.live/api-reference/realtime-communication/create-room).
   *
   * @example
   * ```ts
   * let meeting;
   *
   * // Initialize Meeting
   * meeting = VideoSDK.initMeeting({
   *   // ...
   * });
   *
   * const agentParticipant = Array.from(meeting.participants.values()).find(
   *   (p) => p.isAgent
   * );
   *
   * const audioStats = await agentParticipant.getAudioStats();
   * ```
   */
  getAudioStats(): Promise<
    Array<{
      bitrate: number;
      rtt: number;
      network: string;
      codec: string;
      jitter: number;
      totalPackets: number;
      packetsLost: number;
    }>
  >;

  /**
   * Registers an event listener.
   * @param eventType Event name to which you want to subscribe.
   * @param listener A callback function that is executed when the specified event is emitted.
   *
   * To view the complete list of available events and their details, refer to {@link AgentParticipantEvent}.
   */
  on(
    eventType:
      | "stream-enabled"
      | "stream-disabled"
      | "media-status-changed"
      | "agent-state-changed"
      | "agent-transcription-received"
      | "agent-metrics",
    listener: (data: any) => void,
  ): void;

  /**
   * Removes an event listener that was previously registered.
   *
   * @param eventType Event name to which you want to unsubscribe.
   * @param listener Callback function which was passed while subscribing to the event.
   *
   * To view the complete list of available events and their details, refer to  {@link AgentParticipantEvent}.
   */
  off(
    eventType:
      | "stream-enabled"
      | "stream-disabled"
      | "media-status-changed"
      | "agent-state-changed"
      | "agent-transcription-received"
      | "agent-metrics",
    listener: (data: any) => void,
  ): void;
}

export type AgentParticipantEvent = {
  /**
   * @event
   * Triggered when a participant’s audio, video, or screen-share {@link Stream} is enabled.
   *
   * @param stream
   * The stream that was enabled.
   *
   * @example
   * ```ts
   * agentParticipant.on("stream-enabled", (stream) => {
   *   //
   * });
   * ```
   * @returns
   */
  "stream-enabled": (stream: Stream) => void;

  /**
   * @event
   * Triggered when a participant’s audio, video, or screen-share {@link Stream} is disabled.
   *
   * @param stream
   * The stream that was disabled.
   *
   * @example
   * ```ts
   * agentParticipant.on("stream-disabled", (stream) => {
   *   //
   * });
   * ```
   * @returns
   */
  "stream-disabled": (stream: Stream) => void;

  /**
   * @event
   * Triggered when the media status of a participant changes (for example, when audio or video is enabled or disabled).
   *
   * @param data
   *
   * @param data.kind
   * Type of stream whose status changed.
   *
   * @param data.newStatus
   * The updated status of the stream.
   *
   * @example
   * ```ts
   * agentParticipant.on("media-status-changed", (data) => {
   *   const { kind, newStatus } = data;
   * });
   * ```
   * @returns
   */
  "media-status-changed": (data: { kind: string; newStatus: string }) => void;

  /**
   * @event
   *
   * Triggered when the state of the agentParticipant changes.
   *
   * @param data
   *
   * @param data.state
   * This represents the current state of the agentParticipant.
   *
   * **Possible values:** {@link AgentState}
   *
   * @example
   * ```
   * agentParticipant?.on("agent-state-changed", (data) => {
   *   const { state } = data;
   *
   *   if (state === VideoSDK.Constants.AgentState.SPEAKING) {
   *     console.log("Agent is speaking...");
   *   }
   *
   * });
   * ```
   * @returns
   */
  "agent-state-changed": (data: { state: string }) => void;

  /**
   * @event
   *
   * Triggered when a transcription is received from a participant or agent.
   *
   * @param data
   *
   * @param data.segment
   * This represents the transcription segment.
   *
   * @param data.segment.text
   * This represents the transcribed text.
   *
   * @param data.segment.timestamp
   * This represents the timestamp of the transcription.
   * 
   * @param data.segment.type
   * This represents the type of the transcription segment (e.g., "final", "intrim").
   *
   * @param data.participant
   * This represents the participant whose speech is transcribed. It can be either an AgentParticipant or a regular Participant, depending on who is speaking.
   *
   * @example
   * ```
   * agentParticipant?.on("agent-transcription-received", (data) => {
   *   const { segment, participant } = data;
   *   const { text, timestamp } = segment;
   *
   *   console.log(`${participant.displayName} said "${text}" at ${timestamp}`);
   * });
   * ```
   *
   * @returns
   */
  "agent-transcription-received": (data: {
    segment: { text: string; timestamp: number; type?: string };
    participant: Participant | AgentParticipant;
  }) => void;

  /**
   * @event
   *
   * Triggered after each conversational turn with metrics and speech data.
   *
   * @param data
   *
   * @param data.latency
   * This represents the latency metrics for the current turn.
   *
   * @param data.speech
   * This represents the user and agent speech for the current turn.
   *
   * @param data.providers
   * This represents the providers used (e.g., TTS, STT, LLMs).
   * This is available only in the first turn.
   *
   * @param data.systemInstructions
   * This represents the system instructions configured for the agent.
   * This is available only in the first turn.
   *
   * @example
   * ```
   * agentParticipant?.on("agent-metrics", (data) => {
   *   const { latency, speech, providers, systemInstructions } = data;
   *
   *   console.log("Latency:", latency);
   *   console.log("Speech:", speech);
   * });
   * ```
   *
   * @returns
   */
  "agent-metrics": (data: {
    latency: object;
    speech: object;
    providers?: object;
    systemInstructions?: object;
  }) => void;
};

import { Stream } from "./stream";
import { Participant } from "./participant";
