// Copyright 2017 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 Host from '../../core/host/host.js';
import * as Platform from '../../core/platform/platform.js';
import {html} from '../lit/lit.js';
import * as VisualLogging from '../visual_logging/visual_logging.js';

import * as ARIAUtils from './ARIAUtils.js';
import type {ContextMenu, Provider} from './ContextMenu.js';
import {html as xhtml} from './Fragment.js';
import {Tooltip} from './Tooltip.js';
import {
  copyLinkAddressLabel,
  MaxLengthForDisplayedURLs,
  openInNewTab,
  openLinkExternallyLabel,
} from './UIUtils.js';
import {XElement} from './XElement.js';

export class XLink extends XElement {
  #href: Platform.DevToolsPath.UrlString|null;
  private clickable: boolean;
  private readonly onClick: (arg0: Event) => void;
  private readonly onKeyDown: (arg0: KeyboardEvent) => void;
  static create(
      url: string, linkText?: string, className?: string, preventClick?: boolean, jsLogContext?: string,
      tabindex = '0'): HTMLElement {
    if (!linkText) {
      linkText = url;
    }
    className = className || '';
    // clang-format off
    // TODO(dgozman): migrate css from 'devtools-link' to 'x-link'.
    const element = xhtml `
  <x-link href='${url}' tabindex='${tabindex}' class='${className} devtools-link' ${preventClick ? 'no-click' : ''}
  jslog=${VisualLogging.link().track({click: true, keydown:'Enter|Space'}).context(jsLogContext)}>${Platform.StringUtilities.trimMiddle(linkText, MaxLengthForDisplayedURLs)}</x-link>`;
    // clang-format on
    return element as HTMLElement;
  }

  constructor() {
    super();

    this.style.setProperty('display', 'inline');
    ARIAUtils.markAsLink(this);
    this.setAttribute('tabindex', '0');
    this.setAttribute('target', '_blank');
    this.setAttribute('rel', 'noopener');

    this.#href = null;
    this.clickable = true;

    this.onClick = (event: Event) => {
      event.consume(true);
      if (this.#href) {
        openInNewTab(this.#href);
      }
      this.dispatchEvent(new Event('x-link-invoke'));
    };
    this.onKeyDown = (event: KeyboardEvent) => {
      if (Platform.KeyboardUtilities.isEnterOrSpaceKey(event)) {
        event.consume(true);
        if (this.#href) {
          openInNewTab(this.#href);
        }
      }
      this.dispatchEvent(new Event('x-link-invoke'));
    };
  }

  static override get observedAttributes(): string[] {
    // TODO(dgozman): should be super.observedAttributes, but it does not compile.
    return XElement.observedAttributes.concat(['href', 'no-click', 'title', 'tabindex']);
  }

  get href(): Platform.DevToolsPath.UrlString|null {
    return this.#href;
  }

  override attributeChangedCallback(attr: string, oldValue: string|null, newValue: string|null): void {
    if (attr === 'no-click') {
      this.clickable = !newValue;
      this.updateClick();
      return;
    }

    if (attr === 'href') {
      // For invalid or non-absolute URLs, `href` should remain `null`.
      if (!newValue) {
        newValue = '';
      }
      let href: Platform.DevToolsPath.UrlString|null = null;
      try {
        const url = new URL(newValue);
        if (url.protocol !== 'javascript:') {
          href = Platform.DevToolsPath.urlString`${url}`;
        }
      } catch {
      }

      this.#href = href;
      if (!this.hasAttribute('title')) {
        Tooltip.install(this, newValue);
      }
      this.updateClick();
      return;
    }

    if (attr === 'tabindex') {
      if (oldValue !== newValue) {
        this.setAttribute('tabindex', newValue || '0');
      }
      return;
    }

    super.attributeChangedCallback(attr, oldValue, newValue);
  }

  private updateClick(): void {
    if (this.#href !== null && this.clickable) {
      this.addEventListener('click', this.onClick, false);
      this.addEventListener('keydown', this.onKeyDown, false);
      this.style.setProperty('cursor', 'pointer');
    } else {
      this.removeEventListener('click', this.onClick, false);
      this.removeEventListener('keydown', this.onKeyDown, false);
      this.style.removeProperty('cursor');
    }
  }
}

export class ContextMenuProvider implements Provider<Node> {
  appendApplicableItems(_event: Event, contextMenu: ContextMenu, target: Node): void {
    let targetNode: Node|null = target;
    while (targetNode && !(targetNode instanceof XLink)) {
      targetNode = targetNode.parentNodeOrShadowHost();
    }
    if (!targetNode?.href) {
      return;
    }
    const node: XLink = targetNode;
    contextMenu.revealSection().appendItem(openLinkExternallyLabel(), () => {
      if (node.href) {
        openInNewTab(node.href);
      }
    }, {jslogContext: 'open-in-new-tab'});
    contextMenu.revealSection().appendItem(copyLinkAddressLabel(), () => {
      if (node.href) {
        Host.InspectorFrontendHost.InspectorFrontendHostInstance.copyText(node.href);
      }
    }, {jslogContext: 'copy-link-address'});
  }
}

customElements.define('x-link', XLink);

export const sample = html`<p>Hello, <x-link>world!</x-link></p>`;
