// Copyright 2025 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 '../../ui/components/tooltips/tooltips.js';
import '../../ui/kit/kit.js';

import * as Host from '../../core/host/host.js';
import * as i18n from '../../core/i18n/i18n.js';
import type * as AiCodeCompletion from '../../models/ai_code_completion/ai_code_completion.js';
import * as UI from '../../ui/legacy/legacy.js';
import {Directives, html, nothing, render} from '../../ui/lit/lit.js';

import {AiCodeCompletionDisclaimer} from './AiCodeCompletionDisclaimer.js';
import styles from './aiCodeCompletionSummaryToolbar.css.js';

const UIStringsNotTranslate = {
  /**
   * @description Text for recitation notice
   */
  generatedCodeMayBeSubjectToALicense: 'Generated code may be subject to a license.',
  /**
   * @description Text for citations
   */
  viewSources: 'View Sources',
} as const;

const lockedString = i18n.i18n.lockedString;
const {widget} = UI.Widget;

export interface AiCodeCompletionSummaryToolbarProps {
  citationsTooltipId: string;
  disclaimerTooltipId?: string;
  spinnerTooltipId?: string;
  hasTopBorder?: boolean;
  panel: AiCodeCompletion.AiCodeCompletion.ContextFlavor;
}

export interface ViewInput {
  disclaimerTooltipId?: string;
  spinnerTooltipId?: string;
  citations?: Set<string>;
  citationsTooltipId: string;
  loading: boolean;
  hasTopBorder: boolean;
  aidaAvailability?: Host.AidaClient.AidaAccessPreconditions;
  panel: AiCodeCompletion.AiCodeCompletion.ContextFlavor;
}

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

export const DEFAULT_SUMMARY_TOOLBAR_VIEW: View = (input, _output, target) => {
  if (input.aidaAvailability !== Host.AidaClient.AidaAccessPreconditions.AVAILABLE) {
    render(nothing, target);
    return;
  }
  const toolbarClasses = Directives.classMap({
    'ai-code-completion-summary-toolbar': true,
    'has-disclaimer': Boolean(input.disclaimerTooltipId),
    'has-recitation-notice': Boolean(input.citations && input.citations.size > 0),
    'has-top-border': input.hasTopBorder,
  });

  // clang-format off
  const disclaimer = input.disclaimerTooltipId && input.spinnerTooltipId ?
    html`<devtools-widget
            ${widget(AiCodeCompletionDisclaimer, {
      disclaimerTooltipId: input.disclaimerTooltipId,
      spinnerTooltipId: input.spinnerTooltipId,
      loading: input.loading,
      panel: input.panel,
    })} class="disclaimer-widget"></devtools-widget>` : nothing;

  const recitationNotice = input.citations && input.citations.size > 0 ?
    html`<div class="ai-code-completion-recitation-notice">
                ${lockedString(UIStringsNotTranslate.generatedCodeMayBeSubjectToALicense)}
                <span class="link"
                    role="link"
                    aria-details=${input.citationsTooltipId}
                    aria-describedby=${input.citationsTooltipId}
                    tabIndex="0">
                  ${lockedString(UIStringsNotTranslate.viewSources)}&nbsp;${lockedString('(' + input.citations.size + ')')}
                </span>
                <devtools-tooltip
                    id=${input.citationsTooltipId}
                    variant="rich"
                    jslogContext="ai-code-completion-citations"
                ><div class="citations-tooltip-container">
                    ${Directives.repeat(input.citations, citation => html`<devtools-link
                        tabIndex="0"
                        href=${citation}
                        jslogcontext="ai-code-completion-citations.citation-link">${citation}</devtools-link>`)}</div></devtools-tooltip>
            </div>` : nothing;

  render(
    html`
        <style>${styles}</style>
        <div class=${toolbarClasses}>
          ${disclaimer}
          ${recitationNotice}
        </div>
        `, target);
  // clang-format on
};

export class AiCodeCompletionSummaryToolbar extends UI.Widget.Widget {
  readonly #view: View;

  #disclaimerTooltipId?: string;
  #spinnerTooltipId?: string;
  #citationsTooltipId: string;
  #citations = new Set<string>();
  #loading = false;
  #hasTopBorder = false;
  #panel: AiCodeCompletion.AiCodeCompletion.ContextFlavor;

  #aidaAvailability?: Host.AidaClient.AidaAccessPreconditions;
  #boundOnAidaAvailabilityChange: () => Promise<void>;

  constructor(props: AiCodeCompletionSummaryToolbarProps, view?: View) {
    super();
    this.#disclaimerTooltipId = props.disclaimerTooltipId;
    this.#spinnerTooltipId = props.spinnerTooltipId;
    this.#citationsTooltipId = props.citationsTooltipId;
    this.#hasTopBorder = props.hasTopBorder ?? false;
    this.#panel = props.panel;
    this.#boundOnAidaAvailabilityChange = this.#onAidaAvailabilityChange.bind(this);
    this.#view = view ?? DEFAULT_SUMMARY_TOOLBAR_VIEW;
    this.requestUpdate();
  }

  async #onAidaAvailabilityChange(): Promise<void> {
    const currentAidaAvailability = await Host.AidaClient.AidaClient.checkAccessPreconditions();
    if (currentAidaAvailability !== this.#aidaAvailability) {
      this.#aidaAvailability = currentAidaAvailability;
      this.requestUpdate();
    }
  }

  setLoading(loading: boolean): void {
    this.#loading = loading;
    this.requestUpdate();
  }

  updateCitations(citations: string[]): void {
    citations.forEach(citation => this.#citations.add(citation));
    this.requestUpdate();
  }

  clearCitations(): void {
    this.#citations.clear();
    this.requestUpdate();
  }

  override performUpdate(): void {
    this.#view(
        {
          disclaimerTooltipId: this.#disclaimerTooltipId,
          spinnerTooltipId: this.#spinnerTooltipId,
          citations: this.#citations,
          citationsTooltipId: this.#citationsTooltipId,
          loading: this.#loading,
          hasTopBorder: this.#hasTopBorder,
          aidaAvailability: this.#aidaAvailability,
          panel: this.#panel,
        },
        undefined, this.contentElement);
  }

  override wasShown(): void {
    super.wasShown();
    Host.AidaClient.HostConfigTracker.instance().addEventListener(
        Host.AidaClient.Events.AIDA_AVAILABILITY_CHANGED, this.#boundOnAidaAvailabilityChange);
    void this.#onAidaAvailabilityChange();
  }

  override willHide(): void {
    super.willHide();
    Host.AidaClient.HostConfigTracker.instance().removeEventListener(
        Host.AidaClient.Events.AIDA_AVAILABILITY_CHANGED, this.#boundOnAidaAvailabilityChange);
  }
}
