// 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 '../../../ui/components/spinners/spinners.js';

import * as Host from '../../../core/host/host.js';
import * as i18n from '../../../core/i18n/i18n.js';
import * as Buttons from '../../../ui/components/buttons/buttons.js';
import * as Snackbars from '../../../ui/components/snackbars/snackbars.js';
import * as UI from '../../../ui/legacy/legacy.js';
import * as Lit from '../../../ui/lit/lit.js';
import * as VisualLogging from '../../../ui/visual_logging/visual_logging.js';

import styles from './exportForAgentsDialog.css.js';

const {html, render} = Lit;

const UIStrings = {
  /**
   * @description Title for the export for agents dialog.
   */
  exportForAgents: 'Copy to coding agent',
  /**
   * @description Button text for copying to clipboard.
   */
  copyToClipboard: 'Copy to clipboard',
  /**
   * @description Text displayed in a toast to indicate that the content was copied to the clipboard.
   */
  copiedToClipboard: 'Copied to clipboard',
  /**
   * @description Label for the 'as prompt' radio button in the export for agents dialog.
   */
  asPrompt: 'As prompt',
  /**
   * @description Label for the 'as markdown' radio button in the export for agents dialog.
   */
  asMarkdown: 'As markdown',
  /**
   * @description Button text for saving content as a markdown file.
   */
  saveAsMarkdown: 'Save as…',
  /**
   * @description Text displayed while the summary is being generated.
   */
  generatingSummary: 'Generating summary…',
  /**
   * @description Disclaimer text for the export for agents dialog.
   */
  disclaimer:
      'This is an experimental AI feature and won’t always get it right. Double check this text before pasting into another tool.',
} as const;

const str_ = i18n.i18n.registerUIStrings('panels/ai_assistance/components/ExportForAgentsDialog.ts', UIStrings);
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);

export const enum StateType {
  PROMPT = 'prompt',
  CONVERSATION = 'conversation',
}

const DEFAULT_STATE_TYPE = StateType.PROMPT;

export interface State {
  activeType: StateType;
  promptText: string;
  conversationText: string;
  isPromptLoading: boolean;
}

interface ViewInput {
  onButtonClick: (event: Event) => void;
  state: State;
  onStateChange: (stateType: StateType) => void;
  jslogContext: string;
}

type View = (input: ViewInput, output: undefined, target: HTMLElement) => void;

export const DEFAULT_VIEW: View = (input, _output, target): void => {
  const isPrompt = input.state.activeType === StateType.PROMPT;
  const buttonText = isPrompt ? i18nString(UIStrings.copyToClipboard) : i18nString(UIStrings.saveAsMarkdown);
  const exportText = isPrompt ? input.state.promptText : input.state.conversationText;
  // clang-format off

  render(html`
    <style>${styles}</style>
    <div class="export-for-agents-dialog" jslog=${VisualLogging.dialog('ai-export-for-agents')}>
      <header>
        <h1 id="export-for-agents-dialog-title" tabindex="-1">
          ${i18nString(UIStrings.exportForAgents)}
        </h1>
      </header>
      <div class="state-selection" role="radiogroup" aria-labelledby="export-for-agents-dialog-title">
        <label>
          <input
            type="radio"
            value="prompt"
            name="export-state"
            .checked=${isPrompt}
            aria-label=${i18nString(UIStrings.asPrompt)}
            @change=${() => input.onStateChange(StateType.PROMPT)}
          >
          ${i18nString(UIStrings.asPrompt)}
        </label>
        <label>
          <input
            type="radio"
            value="conversation"
            name="export-state"
            .checked=${!isPrompt}
            aria-label=${i18nString(UIStrings.asMarkdown)}
            @change=${() => input.onStateChange(StateType.CONVERSATION)}
          >
          ${i18nString(UIStrings.asMarkdown)}
        </label>
      </div>
      <main>
        ${isPrompt && input.state.isPromptLoading ? html`
          <span class="prompt-loading">
            <devtools-spinner></devtools-spinner>
            ${i18nString(UIStrings.generatingSummary)}
          </span>
          ` : Lit.nothing}
        <textarea readonly .value=${isPrompt && input.state.isPromptLoading ? '' : exportText}></textarea>
      </main>
      <div class="disclaimer">${i18nString(UIStrings.disclaimer)}</div>
      <footer>
        <div class="right-buttons">
          <devtools-button
            @click=${input.onButtonClick}
            .jslogContext=${input.jslogContext}
            .variant=${Buttons.Button.Variant.PRIMARY}
            .disabled=${isPrompt && input.state.isPromptLoading}
            .accessibleLabel=${buttonText}
          >
            ${buttonText}
          </devtools-button>
        </div>
      </footer>
    </div>
  `, target);
  // clang-format on
};

export class ExportForAgentsDialog extends UI.Widget.VBox {
  static #lastSelectedType: StateType = DEFAULT_STATE_TYPE;
  readonly #view: View;
  readonly #dialog: UI.Dialog.Dialog;
  #state: State;
  #onConversationSaveAs: () => void;

  constructor(
      options: {
        dialog: UI.Dialog.Dialog,
        promptText: string|Promise<string>,
        markdownText: string,
        onConversationSaveAs: () => void,
      },
      view: View = DEFAULT_VIEW) {
    super();
    this.#dialog = options.dialog;
    this.#state = {
      activeType: ExportForAgentsDialog.#lastSelectedType,
      promptText: typeof options.promptText === 'string' ? options.promptText : '',
      conversationText: options.markdownText,
      isPromptLoading: typeof options.promptText !== 'string',
    };
    this.#onConversationSaveAs = options.onConversationSaveAs;
    this.#view = view;

    if (typeof options.promptText !== 'string') {
      void options.promptText.then(promptText => {
        this.#state.promptText = promptText;
        this.#state.isPromptLoading = false;
        this.requestUpdate();
      });
    }

    this.requestUpdate();
  }

  static clearPersistedViewState(): void {
    ExportForAgentsDialog.#lastSelectedType = DEFAULT_STATE_TYPE;
  }

  #onStateChange = (newState: StateType): void => {
    this.#state.activeType = newState;
    ExportForAgentsDialog.#lastSelectedType = newState;
    this.requestUpdate();
  };

  override performUpdate(): void {
    let onButtonClick: (event: Event) => void;
    let jslogContext = '';

    switch (this.#state.activeType) {
      case StateType.PROMPT:
        jslogContext = 'ai-export-for-agents.copy-to-clipboard';
        onButtonClick = (event: Event): void => {
          event.preventDefault();
          Host.InspectorFrontendHost.InspectorFrontendHostInstance.copyText(this.#state.promptText);
          const snackbar = Snackbars.Snackbar.Snackbar.show({
            message: i18nString(UIStrings.copiedToClipboard),
          });
          snackbar.setAttribute('aria-label', i18nString(UIStrings.copiedToClipboard));
          this.#dialog.hide();
        };
        break;
      case StateType.CONVERSATION:
        jslogContext = 'ai-export-for-agents.save-as-markdown';
        onButtonClick = (): void => {
          this.#dialog.hide();
          this.#onConversationSaveAs();
        };
        break;
    }

    const viewInput = {
      onButtonClick,
      state: this.#state,
      onStateChange: this.#onStateChange,
      jslogContext,
    };

    this.#view(viewInput, undefined, this.contentElement);
  }

  static show({
    promptText,
    markdownText,
    onConversationSaveAs,
  }: {
    promptText: string|Promise<string>,
    markdownText: string,
    onConversationSaveAs: () => void,
  }): void {
    const dialog = new UI.Dialog.Dialog();
    dialog.setAriaLabel(i18nString(UIStrings.exportForAgents));

    dialog.setOutsideClickCallback(ev => {
      ev.consume(true);
      dialog.hide();
    });

    dialog.addCloseButton();
    dialog.setSizeBehavior(UI.GlassPane.SizeBehavior.MEASURE_CONTENT);
    dialog.setDimmed(true);

    const exportDialog = new ExportForAgentsDialog({dialog, promptText, markdownText, onConversationSaveAs});
    exportDialog.show(dialog.contentElement);
    // Because the Dialog sets its height based off its content, we need
    // the Export Dialog widget to have rendered fully before we show
    // the dialog with its contents.
    void exportDialog.updateComplete.then(() => {
      dialog.show();
    });
  }
}
