// Copyright 2016 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/legacy/legacy.js';

import type * as Common from '../../core/common/common.js';
import * as i18n from '../../core/i18n/i18n.js';
import * as Platform from '../../core/platform/platform.js';
import * as SDK from '../../core/sdk/sdk.js';
import type * as TextUtils from '../../models/text_utils/text_utils.js';
import * as UI from '../../ui/legacy/legacy.js';
import {html, render} from '../../ui/lit/lit.js';
import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';

import {DeveloperResourcesListView} from './DeveloperResourcesListView.js';
import developerResourcesViewStyles from './developerResourcesView.css.js';

const {widget} = UI.Widget;
const {bindToSetting} = UI.UIUtils;

const UIStrings = {
  /**
   * @description Placeholder for a search field in a toolbar
   */
  filterByText: 'Filter by URL and error',
  /**
   * @description Tooltip for a checkbox in the toolbar of the developer resources view. The
   * inspected target is the webpage that DevTools is debugging/inspecting/attached to.
   */
  loadHttpsDeveloperResources:
      'Load `HTTP(S)` developer resources through the website you inspect, not through DevTools',
  /**
   * @description Text for a checkbox in the toolbar of the developer resources view. The target is
   * the webpage that DevTools is debugging/inspecting/attached to. This setting makes it so
   * developer resources are requested from the webpage itself, and not from the DevTools
   * application.
   */
  enableLoadingThroughTarget: 'Load through website',
  /**
   * @description Text for resources load status
   * @example {1} PH1
   * @example {1} PH2
   */
  resourcesCurrentlyLoading: '{PH1} resources, {PH2} currently loading',
  /**
   * @description Status text that appears to tell the developer how many resources were loaded in
   * total. Resources are files related to the webpage.
   */
  resources: '{n, plural, =1 {# resource} other {# resources}}',
} as const;
const str_ = i18n.i18n.registerUIStrings('panels/developer_resources/DeveloperResourcesView.ts', UIStrings);
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
export class DeveloperResourcesRevealer implements Common.Revealer.Revealer<SDK.PageResourceLoader.ResourceKey> {
  async reveal(resourceInitiatorKey: SDK.PageResourceLoader.ResourceKey): Promise<void> {
    const loader = SDK.PageResourceLoader.PageResourceLoader.instance();
    const resource = loader.getResourcesLoaded().get(resourceInitiatorKey.key);
    if (resource) {
      await UI.ViewManager.ViewManager.instance().showView('developer-resources');
      const developerResourcesView =
          await UI.ViewManager.ViewManager.instance().view('developer-resources').widget() as DeveloperResourcesView;
      return await developerResourcesView.select(resource);
    }
  }
}

interface ViewInput {
  onFilterChanged: (e: CustomEvent<string>) => void;
  items: Iterable<SDK.PageResourceLoader.PageResource>;
  selectedItem: SDK.PageResourceLoader.PageResource|null;
  onSelect: (resource: SDK.PageResourceLoader.PageResource|null) => void;
  filters: TextUtils.TextUtils.ParsedFilter[];
  numResources: number;
  numLoading: number;
  loadThroughTargetSetting: Common.Settings.Setting<boolean>;
}

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

export const DEFAULT_VIEW: View = (input, _output, target) => {
  // clang-format off
  render(html`
    <style>
      ${developerResourcesViewStyles}
    </style>
    <div class="vbox flex-auto" jslog=${VisualLogging.panel('developer-resources').track({resize: true})}>
      <div class="developer-resource-view-toolbar-container" jslog=${VisualLogging.toolbar()}
          role="toolbar">
        <devtools-toolbar class="developer-resource-view-toolbar" role="presentation">
          <devtools-toolbar-input type="filter" placeholder=${i18nString(UIStrings.filterByText)}
              @change=${input.onFilterChanged} style="flex-grow:1">
          </devtools-toolbar-input>
          <devtools-checkbox
              title=${i18nString(UIStrings.loadHttpsDeveloperResources)}
              ${bindToSetting(input.loadThroughTargetSetting)}>
            ${i18nString(UIStrings.enableLoadingThroughTarget)}
          </devtools-checkbox>
        </devtools-toolbar>
      </div>
      <div class="developer-resource-view-results">
        ${widget(DeveloperResourcesListView, {
            items: input.items,
            selectedItem: input.selectedItem,
            onSelect: input.onSelect,
            filters: input.filters
        })}
      </div>
      <div class="developer-resource-view-toolbar-summary">
        <div class="developer-resource-view-message">
          ${input.numLoading > 0 ?
              i18nString(UIStrings.resourcesCurrentlyLoading, {PH1: input.numResources, PH2: input.numLoading}) :
              i18nString(UIStrings.resources, {n: input.numResources})}
         </div>
      </div>
    </div>`, target);
  // clang-format on
};

export class DeveloperResourcesView extends UI.Widget.VBox {
  readonly #loader: SDK.PageResourceLoader.PageResourceLoader;
  readonly #view: View;
  #selectedItem: SDK.PageResourceLoader.PageResource|null = null;
  #filters: TextUtils.TextUtils.ParsedFilter[] = [];

  constructor(view: View = DEFAULT_VIEW) {
    super({useShadowDom: true});
    this.#view = view;

    this.#loader = SDK.PageResourceLoader.PageResourceLoader.instance();
    this.#loader.addEventListener(SDK.PageResourceLoader.Events.UPDATE, this.requestUpdate, this);
    this.requestUpdate();
  }

  override async performUpdate(): Promise<void> {
    const {loading, resources} = this.#loader.getScopedNumberOfResources();
    const input = {
      onFilterChanged: (e: CustomEvent<string>) => {
        this.onFilterChanged(e.detail);
      },
      items: this.#loader.getResourcesLoaded().values(),
      selectedItem: this.#selectedItem,
      onSelect: (item: SDK.PageResourceLoader.PageResource|null) => {
        this.#selectedItem = item;
      },
      filters: this.#filters,
      numResources: resources,
      numLoading: loading,
      loadThroughTargetSetting: this.#loader.getLoadThroughTargetSetting(),
    };
    const output = {};
    this.#view(input, output, this.contentElement);
  }

  async select(resource: SDK.PageResourceLoader.PageResource): Promise<void> {
    await this.updateComplete;
    this.#selectedItem = resource;
    this.requestUpdate();
  }

  async selectedItem(): Promise<SDK.PageResourceLoader.PageResource|null> {
    await this.updateComplete;
    return this.#selectedItem;
  }

  private onFilterChanged(text: string): void {
    const textFilterRegExp = text ? Platform.StringUtilities.createPlainTextSearchRegex(text, 'i') : null;
    if (textFilterRegExp) {
      this.#filters = [
        {key: 'url,error-message', regex: textFilterRegExp, negative: false},
      ];
    } else {
      this.#filters = [];
    }
    this.requestUpdate();
  }
}
