// Copyright 2022 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/kit/kit.js';

import * as Host from '../../../core/host/host.js';
import * as i18n from '../../../core/i18n/i18n.js';
import * as Platform from '../../../core/platform/platform.js';
import * as Buttons from '../../../ui/components/buttons/buttons.js';
import * as Input from '../../../ui/components/input/input.js';
import * as uiI18n from '../../../ui/i18n/i18n.js';
import * as UI from '../../../ui/legacy/legacy.js';
import {
  html,
  i18nTemplate as unboundI18nTemplate,
  type LitTemplate,
  nothing,
  render,
  type TemplateResult
} from '../../../ui/lit/lit.js';
import * as VisualLogging from '../../../ui/visual_logging/visual_logging.js';

import protocolHandlersViewStyles from './protocolHandlersView.css.js';

const PROTOCOL_DOCUMENT_URL = 'https://web.dev/url-protocol-handler/';
const UIStrings = {
  /**
   * @description Status message for when protocol handlers are detected in the manifest
   * @example {protocolhandler/manifest.json} PH1
   */
  protocolDetected:
      'Found valid protocol handler registration in the {PH1}. With the app installed, test the registered protocols.',
  /**
   * @description Status message for when protocol handlers are not detected in the manifest
   * @example {protocolhandler/manifest.json} PH1
   */
  protocolNotDetected:
      'Define protocol handlers in the {PH1} to register your app as a handler for custom protocols when your app is installed.',
  /**
   * @description Text wrapping a link pointing to more information on handling protocol handlers
   * @example {https://example.com/} PH1
   */
  needHelpReadOur: 'Need help? Read {PH1}.',
  /**
   * @description Link text for more information on URL protocol handler registrations for PWAs
   */
  protocolHandlerRegistrations: 'URL protocol handler registration for PWAs',
  /**
   * @description In text hyperlink to the PWA manifest
   */
  manifest: 'manifest',
  /**
   * @description Text for test protocol button
   */
  testProtocol: 'Test protocol',
  /**
   * @description Aria text for screen reader to announce they can select a protocol handler in the dropdown
   */
  dropdownLabel: 'Select protocol handler',
  /**
   * @description Aria text for screen reader to announce they can enter query parameters or endpoints into the textbox
   */
  textboxLabel: 'Query parameter or endpoint for protocol handler',
  /**
   * @description Placeholder for textbox input field, rest of the URL of protocol to test.
   */
  textboxPlaceholder: 'Enter URL',
} as const;

const str_ = i18n.i18n.registerUIStrings('panels/application/components/ProtocolHandlersView.ts', UIStrings);
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
const i18nTemplate = unboundI18nTemplate.bind(undefined, str_);

function renderStatusMessage(
    protocolHandlers: readonly ProtocolHandler[], manifestLink: Platform.DevToolsPath.UrlString): TemplateResult {
  const statusString = protocolHandlers.length > 0 ? UIStrings.protocolDetected : UIStrings.protocolNotDetected;
  // clang-format off
  return html`
    <div class="protocol-handlers-row status">
      <devtools-icon class="inline-icon"
                     name=${protocolHandlers.length > 0 ? 'check-circle' : 'info'}>
      </devtools-icon>
      ${uiI18n.getFormatLocalizedStringTemplate(str_, statusString, {PH1: html`
        <devtools-link href=${manifestLink} jslogcontext="manifest">${i18nString(UIStrings.manifest)}</devtools-link>
        ` })}
    </div>`;
  // clang-format on
}

function renderProtocolTest(
    protocolHandlers: readonly ProtocolHandler[], queryInputState: string,
    protocolSelectHandler: (evt: HTMLSelectElementEvent) => void,
    queryInputChangeHandler: (evt: HTMLInputElementEvent) => void, testProtocolClickHandler: () => void): LitTemplate {
  if (protocolHandlers.length === 0) {
    return nothing;
  }
  // clang-format off
  return html`
    <div class="protocol-handlers-row">
      <select class="protocol-select" @change=${protocolSelectHandler}
              aria-label=${i18nString(UIStrings.dropdownLabel)}>
        ${protocolHandlers.filter(p => p.protocol).map(({protocol}) => html`
          <option value=${protocol} jslog=${VisualLogging.item(protocol).track({click: true})}>
            ${protocol}://
          </option>`)}
      </select>
      <input .value=${queryInputState} class="devtools-text-input" type="text"
             @change=${queryInputChangeHandler} aria-label=${i18nString(UIStrings.textboxLabel)}
             placeholder=${i18nString(UIStrings.textboxPlaceholder)} />
      <devtools-button .variant=${Buttons.Button.Variant.PRIMARY} @click=${testProtocolClickHandler}>
        ${i18nString(UIStrings.testProtocol)}
      </devtools-button>
    </div>`;
  // clang-format on
}

interface HTMLSelectElementEvent extends Event {
  target: HTMLSelectElement;
}

interface HTMLInputElementEvent extends Event {
  target: HTMLInputElement;
}

interface ViewInput {
  protocolHandler: ProtocolHandler[];
  manifestLink: Platform.DevToolsPath.UrlString;
  queryInputState: string;
  protocolSelectHandler: (evt: HTMLSelectElementEvent) => void;
  queryInputChangeHandler: (evt: HTMLInputElementEvent) => void;
  testProtocolClickHandler: () => void;
}

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

const DEFAULT_VIEW: View = (input, _output, target) => {
  // inspectorCommonStyles is used for the <select> styling that is used for the dropdown
  // clang-format off
  render(html`
    <style>${protocolHandlersViewStyles}</style>
    <style>${UI.inspectorCommonStyles}</style>
    <style>${Input.textInputStyles}</style>
    ${renderStatusMessage(input.protocolHandler, input.manifestLink)}
    <div class="protocol-handlers-row">
      ${i18nTemplate(UIStrings.needHelpReadOur, {PH1: html`
        <devtools-link href=${PROTOCOL_DOCUMENT_URL} class="devtools-link" autofocus jslogcontext="learn-more">
          ${i18nString(UIStrings.protocolHandlerRegistrations)}
        </devtools-link>`})}
    </div>
    ${renderProtocolTest(input.protocolHandler, input.queryInputState, input.protocolSelectHandler,
                         input.queryInputChangeHandler, input.testProtocolClickHandler)}
  `, target, {container: {classes: ['vbox']}});
  // clang-format on
};

export interface ProtocolHandler {
  protocol: string;
  url: string;
}

export interface ProtocolHandlersData {
  protocolHandlers: ProtocolHandler[];
  manifestLink: Platform.DevToolsPath.UrlString;
}

export class ProtocolHandlersView extends UI.Widget.Widget {
  #protocolHandlers: ProtocolHandler[] = [];
  #manifestLink: Platform.DevToolsPath.UrlString = Platform.DevToolsPath.EmptyUrlString;
  #selectedProtocolState = '';
  #queryInputState = '';
  #view: View;

  constructor(element?: HTMLElement, view: View = DEFAULT_VIEW) {
    super(element, {useShadowDom: false});
    this.#view = view;
  }

  set protocolHandlers(protocolHandlers: ProtocolHandler[]) {
    this.#protocolHandlers = protocolHandlers;
    this.requestUpdate();
  }

  get protocolHandlers(): ProtocolHandler[] {
    return this.#protocolHandlers;
  }

  set manifestLink(manifestLink: Platform.DevToolsPath.UrlString) {
    const isNewManifest = this.#manifestLink !== manifestLink;
    this.#manifestLink = manifestLink;
    if (isNewManifest) {
      this.#queryInputState = '';
      this.#selectedProtocolState = this.#protocolHandlers[0]?.protocol ?? '';
    }
    this.requestUpdate();
  }

  get manifestLink(): Platform.DevToolsPath.UrlString {
    return this.#manifestLink;
  }

  #handleProtocolSelect = (evt: HTMLSelectElementEvent): void => {
    this.#selectedProtocolState = evt.target.value;
  };

  #handleQueryInputChange = (evt: HTMLInputElementEvent): void => {
    this.#queryInputState = evt.target.value;
    this.requestUpdate();
  };

  #handleTestProtocolClick = (): void => {
    const protocolURL = `${this.#selectedProtocolState}://${this.#queryInputState}` as Platform.DevToolsPath.UrlString;
    Host.InspectorFrontendHost.InspectorFrontendHostInstance.openInNewTab(protocolURL);
    Host.userMetrics.actionTaken(Host.UserMetrics.Action.CaptureTestProtocolClicked);
  };

  override performUpdate(): void {
    this.#view(
        {
          protocolHandler: this.#protocolHandlers,
          manifestLink: this.#manifestLink,
          queryInputState: this.#queryInputState,
          protocolSelectHandler: this.#handleProtocolSelect,
          queryInputChangeHandler: this.#handleQueryInputChange,
          testProtocolClickHandler: this.#handleTestProtocolClick,
        },
        undefined, this.contentElement);
  }
}
