// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/* eslint-disable @devtools/no-lit-render-outside-of-view, @devtools/enforce-custom-element-definitions-location */
import '../../kit/kit.js';

import * as VisualLogging from '../../../ui/visual_logging/visual_logging.js';
import * as Lit from '../../lit/lit.js';

import floatingButtonStyles from './floatingButton.css.js';

const {html, Directives: {classMap}} = Lit;

/**
 * A simple floating button component, primarily used to display the 'Ask AI!'
 * teaser when hovering over specific UI elements.
 *
 * Usage is simple:
 *
 * ```js
 * // Instantiate programmatically via the `create()` helper:
 * const button = Buttons.FloatingButton.create(AiAssistance.AiUtils.getIconName(), 'Ask AI!');
 *
 * // Use within a template:
 * html`
 * <devtools-floating-button icon-name="smart-assistant"
 *                           title="Ask AI!">
 * </devtools-floating-button>
 * `;
 * ```
 *
 * @property iconName - The `"icon-name"` attribute is reflected as a property.
 * @property jslogContext - The `"jslogcontext"` attribute is reflected as a property.
 * @attribute icon-name - The basename of the icon file (not including the `.svg`
 *                   suffix).
 * @attribute jslogcontext - The context for the `jslog` attribute. A `jslog`
 *                      attribute is generated automatically with the
 *                      provided context.
 */
export class FloatingButton extends HTMLElement {
  static readonly observedAttributes = ['icon-name', 'jslogcontext'];

  readonly #shadow = this.attachShadow({mode: 'open'});

  constructor() {
    super();
    this.role = 'presentation';
    this.#render();
  }

  /**
   * Yields the value of the `"icon-name"` attribute of this `FloatingButton`
   * (`null` in case there's no `"icon-name"` on this element).
   */
  get iconName(): string|null {
    return this.getAttribute('icon-name');
  }

  /**
   * Changes the value of the `"icon-name"` attribute of this `FloatingButton`.
   * If you pass `null`, the `"icon-name"` attribute will be removed from this
   * element.
   *
   * @param the new icon name or `null` to unset.
   */
  set iconName(iconName: string|null) {
    if (iconName === null) {
      this.removeAttribute('icon-name');
    } else {
      this.setAttribute('icon-name', iconName);
    }
  }

  get jslogContext(): string|null {
    return this.getAttribute('jslogcontext');
  }

  set jslogContext(jslogContext: string|null) {
    if (jslogContext === null) {
      this.removeAttribute('jslogcontext');
    } else {
      this.setAttribute('jslogcontext', jslogContext);
    }
  }

  attributeChangedCallback(name: string, oldValue: string|null, newValue: string|null): void {
    if (oldValue === newValue) {
      return;
    }
    if (name === 'icon-name') {
      this.#render();
    }
    if (name === 'jslogcontext') {
      this.#updateJslog();
    }
  }

  #render(): void {
    const classes = classMap({
      gemini: this.iconName === 'spark',
    });

    // clang-format off
    Lit.render(html`
        <style>${floatingButtonStyles}</style>
        <button class=${classes}><devtools-icon .name=${this.iconName}></devtools-icon></button>`,
        this.#shadow, {host: this});
    // clang-format on
  }

  #updateJslog(): void {
    if (this.jslogContext) {
      this.setAttribute('jslog', `${VisualLogging.action().track({click: true}).context(this.jslogContext)}`);
    } else {
      this.removeAttribute('jslog');
    }
  }
}

/**
 * Helper function to programmatically create a `FloatingButton` instance with a
 * given `iconName` and `title`.
 *
 * @param iconName the name of the icon to use
 * @param title the tooltip for the `FloatingButton`
 * @param jslogContext the context string for the `jslog` attribute
 * @returns the newly created `FloatingButton` instance.
 */
export const create = (iconName: string, title: string, jslogContext?: string): FloatingButton => {
  const floatingButton = new FloatingButton();
  floatingButton.iconName = iconName;
  floatingButton.title = title;
  if (jslogContext) {
    floatingButton.jslogContext = jslogContext;
  }
  return floatingButton;
};

customElements.define('devtools-floating-button', FloatingButton);

declare global {
  interface HTMLElementTagNameMap {
    'devtools-floating-button': FloatingButton;
  }
}
