// 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 Common from '../../core/common/common.js';

export interface EditorAnchorSignature {
  /** 1-based line number for CodeMirror text editor anchors */
  lineNumber: number;
  /** File path associated with the editor */
  filePath?: string;
}

export interface DOMNodeAnchorSignature {
  /** Backend NodeId for DOM nodes (`data-backend-node-id`) */
  backendNodeId: number;
  /** Target ID associated with the DOM node (`data-target-id`) */
  targetId: string;
}

export interface TimelineAnchorSignature {
  /** Identifier of the trace to scope comments to a specific recording */
  traceId: string;
  /** Serializable key from Trace.EventsSerializer (e.g. 'r-123', 'p-1-2-3-4', 's-5') */
  traceEventKey: string;
  /** Primary event title or category name */
  entryName: string;
  /** Event start timestamp in microseconds */
  startTimeMicro: number;
  /** Chart location */
  chartLocation: 'main'|'network';
  /** Event duration in microseconds (omitted for instant markers) */
  durationMicro?: number;
}

export interface CommentAnchorSignature {
  /** Visual logging tree path, e.g. "Panel: elements > Pane: styles > TreeOutline > TreeItem: color" */
  vePath: string;
  /** Normalized text content of the target node */
  textSignature: string;
  /** Text content of the parent container VE node for sibling disambiguation */
  parentTextSignature?: string;
  /** 0-indexed position among siblings sharing the same visual logging path */
  siblingIndex?: number;
  /** Optional backend RequestId for Network panel elements (`data-network-request-id`) */
  networkRequestId?: string;
  /** Optional DOM node identifiers (`data-backend-node-id`, `data-target-id`) */
  node?: DOMNodeAnchorSignature;
  /** Optional editor anchor coordinates for CodeMirror text editors */
  editor?: EditorAnchorSignature;
  /** Optional timeline flamechart anchor coordinates for Performance panel trace entries */
  timeline?: TimelineAnchorSignature;
}

export interface Comment {
  author: 'DEVELOPER'|'AGENT';
  text: string;
  timestamp: number;
}

export interface ChangeRecord {
  id: string;
  description: string;
  timestamp: number;
}

export type CommentThreadStatus = 'DRAFT'|'ACTIVE'|'RESOLVED';

export const enum Events {
  CHANGED = 'Changed',
}

export interface EventTypes {
  [Events.CHANGED]: void;
}

export interface CommentThreadOptions {
  anchor: CommentAnchorSignature;
  comments?: Comment[];
  changes?: ChangeRecord[];
}

export class CommentThread extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
  static #nextIndex = 1;

  static resetIndex(): void {
    CommentThread.#nextIndex = 1;
  }

  readonly id: string = crypto.randomUUID();
  readonly anchor: CommentAnchorSignature;
  #savedIndex?: number;
  comments: Comment[];
  status: CommentThreadStatus = 'DRAFT';
  transmitted: boolean = false;
  changes?: ChangeRecord[];

  constructor(options: CommentThreadOptions) {
    super();
    this.anchor = options.anchor;
    this.comments = options.comments ?? [];
    this.changes = options.changes;
  }

  get index(): number {
    return this.#savedIndex ?? CommentThread.#nextIndex;
  }

  save(text?: string, author: 'DEVELOPER'|'AGENT' = 'DEVELOPER'): void {
    let changed = false;
    if (text && text.trim().length > 0) {
      this.comments.push({
        author,
        text: text.trim(),
        timestamp: Date.now(),
      });
      changed = true;
    }
    if (this.status === 'DRAFT') {
      if (this.#savedIndex === undefined) {
        this.#savedIndex = CommentThread.#nextIndex++;
      }
      this.status = 'ACTIVE';
      changed = true;
    }
    if (changed) {
      this.dispatchEventToListeners(Events.CHANGED);
    }
  }

  resolve(replyText?: string): void {
    if (replyText && replyText.trim().length > 0) {
      this.comments.push({
        author: 'AGENT',
        text: replyText.trim(),
        timestamp: Date.now(),
      });
    }
    if (this.#savedIndex === undefined) {
      this.#savedIndex = CommentThread.#nextIndex++;
    }
    this.status = 'RESOLVED';
    this.dispatchEventToListeners(Events.CHANGED);
  }
}
