// Copyright (c) 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import type * as Platform from '../../../core/platform/platform.js';
import * as i18n from '../../../core/i18n/i18n.js';
import * as Buttons from '../../../ui/components/buttons/buttons.js';
import * as SDK from '../../../core/sdk/sdk.js';
import * as LitHtml from '../../../ui/lit-html/lit-html.js';
import * as Root from '../../../core/root/root.js';
import * as ReportView from '../../../ui/components/report_view/report_view.js';
import * as UI from '../../../ui/legacy/legacy.js';
import * as Protocol from '../../../generated/protocol.js';
import * as IconButton from '../../../ui/components/icon_button/icon_button.js';
import * as ComponentHelpers from '../../../ui/components/helpers/helpers.js';
import * as Coordinator from '../../../ui/components/render_coordinator/render_coordinator.js';
import * as ChromeLink from '../../../ui/components/chrome_link/chrome_link.js';
import * as ExpandableList from '../../../ui/components/expandable_list/expandable_list.js';

import {NotRestoredReasonDescription} from './BackForwardCacheStrings.js';
import backForwardCacheViewStyles from './backForwardCacheView.css.js';

const UIStrings = {
  /**
   * @description Title text in back/forward cache view of the Application panel
   */
  mainFrame: 'Main Frame',
  /**
   * @description Title text in back/forward cache view of the Application panel
   */
  backForwardCacheTitle: 'Back/forward cache',
  /**
   * @description Status text for the status of the main frame
   */
  unavailable: 'unavailable',
  /**
   * @description Entry name text in the back/forward cache view of the Application panel
   */
  url: 'URL:',
  /**
   * @description Status text for the status of the back/forward cache status
   */
  unknown: 'Unknown Status',
  /**
   * @description Status text for the status of the back/forward cache status indicating that
   * the back/forward cache was not used and a normal navigation occured instead.
   */
  normalNavigation:
      'Not served from back/forward cache: to trigger back/forward cache, use Chrome\'s back/forward buttons, or use the test button below to automatically navigate away and back.',
  /**
   * @description Status text for the status of the back/forward cache status indicating that
   * the back/forward cache was used to restore the page instead of reloading it.
   */
  restoredFromBFCache: 'Successfully served from back/forward cache.',
  /**
   * @description Label for a list of reasons which prevent the page from being eligible for
   * back/forward cache. These reasons are actionable i.e. they can be cleaned up to make the
   * page eligible for back/forward cache.
   */
  pageSupportNeeded: 'Actionable',
  /**
   * @description Explanation for actionable items which prevent the page from being eligible
   * for back/forward cache.
   */
  pageSupportNeededExplanation:
      'These reasons are actionable i.e. they can be cleaned up to make the page eligible for back/forward cache.',
  /**
   * @description Label for a list of reasons which prevent the page from being eligible for
   * back/forward cache. These reasons are circumstantial / not actionable i.e. they cannot be
   * cleaned up by developers to make the page eligible for back/forward cache.
   */
  circumstantial: 'Not Actionable',
  /**
   * @description Explanation for circumstantial/non-actionable items which prevent the page from being eligible
   * for back/forward cache.
   */
  circumstantialExplanation:
      'These reasons are not actionable i.e. caching was prevented by something outside of the direct control of the page.',
  /**
   * @description Label for a list of reasons which prevent the page from being eligible for
   * back/forward cache. These reasons are pending support by chrome i.e. in a future version
   * of chrome they will not prevent back/forward cache usage anymore.
   */
  supportPending: 'Pending Support',
  /**
   * @description Label for the button to test whether BFCache is available for the page
   */
  runTest: 'Test back/forward cache',
  /**
   * @description Label for the disabled button while the test is running
   */
  runningTest: 'Running test',
  /**
   * @description Link Text about explanation of back/forward cache
   */
  learnMore: 'Learn more: back/forward cache eligibility',
  /**
   * @description Link Text about unload handler
   */
  neverUseUnload: 'Learn more: Never use unload handler',
  /**
   * @description Explanation for 'pending support' items which prevent the page from being eligible
   * for back/forward cache.
   */
  supportPendingExplanation:
      'Chrome support for these reasons is pending i.e. they will not prevent the page from being eligible for back/forward cache in a future version of Chrome.',
  /**
   * @description Text that precedes displaying a link to the extension which blocked the page from being eligible for back/forward cache.
   */
  blockingExtensionId: 'Extension id: ',
  /**
   * @description Label for the 'Frames' section of the back/forward cache view, which shows a frame tree of the
   * page with reasons why the frames can't be cached.
   */
  framesTitle: 'Frames',
  /**
   * @description Top level summary of the total number of issues found in a single frame.
   */
  issuesInSingleFrame: '{n, plural, =1 {# issue found in 1 frame.} other {# issues found in 1 frame.}}',
  /**
   * @description Top level summary of the total number of issues found and the number of frames they were found in.
   * 'm' is never less than 2.
   * @example {3} m
   */
  issuesInMultipleFrames: '{n, plural, =1 {# issue found in {m} frames.} other {# issues found in {m} frames.}}',
  /**
   * @description Shows the number of frames with a particular issue.
   */
  framesPerIssue: '{n, plural, =1 {# frame} other {# frames}}',
  /**
  *@description Title for a frame in the frame tree that doesn't have a URL. Placeholder indicates which number frame with a blank URL it is.
  *@example {3} PH1
  */
  blankURLTitle: 'Blank URL [{PH1}]',
};

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

const enum ScreenStatusType {
  Running = 'Running',
  Result = 'Result',
}

export class BackForwardCacheViewWrapper extends UI.ThrottledWidget.ThrottledWidget {
  readonly #bfcacheView = new BackForwardCacheView();

  constructor() {
    super(true, 1000);
    this.#getMainResourceTreeModel()?.addEventListener(
        SDK.ResourceTreeModel.Events.MainFrameNavigated, this.update, this);
    this.#getMainResourceTreeModel()?.addEventListener(
        SDK.ResourceTreeModel.Events.BackForwardCacheDetailsUpdated, this.update, this);
    this.contentElement.classList.add('overflow-auto');
    this.contentElement.appendChild(this.#bfcacheView);
    this.update();
  }

  async doUpdate(): Promise<void> {
    this.#bfcacheView.data = {frame: this.#getMainFrame()};
  }

  #getMainResourceTreeModel(): SDK.ResourceTreeModel.ResourceTreeModel|null {
    const mainTarget = SDK.TargetManager.TargetManager.instance().mainTarget();
    return mainTarget?.model(SDK.ResourceTreeModel.ResourceTreeModel) || null;
  }

  #getMainFrame(): SDK.ResourceTreeModel.ResourceTreeFrame|null {
    return this.#getMainResourceTreeModel()?.mainFrame || null;
  }
}

const coordinator = Coordinator.RenderCoordinator.RenderCoordinator.instance();

export interface BackForwardCacheViewData {
  frame: SDK.ResourceTreeModel.ResourceTreeFrame|null;
}

export class BackForwardCacheView extends HTMLElement {
  static readonly litTagName = LitHtml.literal`devtools-resources-back-forward-cache-view`;
  readonly #shadow = this.attachShadow({mode: 'open'});
  #frame: SDK.ResourceTreeModel.ResourceTreeFrame|null = null;
  #screenStatus = ScreenStatusType.Result;

  connectedCallback(): void {
    this.#shadow.adoptedStyleSheets = [backForwardCacheViewStyles];
  }

  set data(data: BackForwardCacheViewData) {
    this.#frame = data.frame;
    void this.#render();
  }

  async #render(): Promise<void> {
    await coordinator.write('BackForwardCacheView render', () => {
      // Disabled until https://crbug.com/1079231 is fixed.
      // clang-format off
      LitHtml.render(LitHtml.html`
        <${ReportView.ReportView.Report.litTagName} .data=${
            {reportTitle: i18nString(UIStrings.backForwardCacheTitle)} as ReportView.ReportView.ReportData
        }>
          ${this.#renderMainFrameInformation()}
        </${ReportView.ReportView.Report.litTagName}>
      `, this.#shadow, {host: this});
      // clang-format on
    });
  }

  #renderBackForwardCacheTestResult(): void {
    SDK.TargetManager.TargetManager.instance().removeModelListener(
        SDK.ResourceTreeModel.ResourceTreeModel, SDK.ResourceTreeModel.Events.FrameNavigated,
        this.#renderBackForwardCacheTestResult, this);
    this.#screenStatus = ScreenStatusType.Result;
    void this.#render();
  }

  async #goBackOneHistoryEntry(): Promise<void> {
    SDK.TargetManager.TargetManager.instance().removeModelListener(
        SDK.ResourceTreeModel.ResourceTreeModel, SDK.ResourceTreeModel.Events.FrameNavigated,
        this.#goBackOneHistoryEntry, this);
    this.#screenStatus = ScreenStatusType.Running;
    void this.#render();
    const mainTarget = SDK.TargetManager.TargetManager.instance().mainTarget();
    if (!mainTarget) {
      return;
    }
    const resourceTreeModel = mainTarget.model(SDK.ResourceTreeModel.ResourceTreeModel);
    if (!resourceTreeModel) {
      return;
    }
    const historyResults = await resourceTreeModel.navigationHistory();
    if (!historyResults) {
      return;
    }
    SDK.TargetManager.TargetManager.instance().addModelListener(
        SDK.ResourceTreeModel.ResourceTreeModel, SDK.ResourceTreeModel.Events.FrameNavigated,
        this.#renderBackForwardCacheTestResult, this);
    resourceTreeModel.navigateToHistoryEntry(historyResults.entries[historyResults.currentIndex - 1]);
  }

  async #navigateAwayAndBack(): Promise<void> {
    // Checking BFCache Compatibility

    const mainTarget = SDK.TargetManager.TargetManager.instance().mainTarget();
    if (!mainTarget) {
      return;
    }
    const resourceTreeModel = mainTarget.model(SDK.ResourceTreeModel.ResourceTreeModel);

    if (resourceTreeModel) {
      // This event is removed by inside of goBackOneHistoryEntry().
      SDK.TargetManager.TargetManager.instance().addModelListener(
          SDK.ResourceTreeModel.ResourceTreeModel, SDK.ResourceTreeModel.Events.FrameNavigated,
          this.#goBackOneHistoryEntry, this);

      // We can know whether the current page can use BFCache
      // as the browser navigates to another unrelated page and goes back to the current page.
      // We chose "chrome://terms" because it must be cross-site.
      // Ideally, We want to have our own testing page like "chrome: //bfcache-test".
      void resourceTreeModel.navigate('chrome://terms' as Platform.DevToolsPath.UrlString);
    }
  }

  #renderMainFrameInformation(): LitHtml.TemplateResult {
    if (!this.#frame) {
      // clang-format off
      return LitHtml.html`
        <${ReportView.ReportView.ReportKey.litTagName}>
          ${i18nString(UIStrings.mainFrame)}
        </${ReportView.ReportView.ReportKey.litTagName}>
        <${ReportView.ReportView.ReportValue.litTagName}>
          ${i18nString(UIStrings.unavailable)}
        </${ReportView.ReportView.ReportValue.litTagName}>
      `;
      // clang-format on
    }
    const isTestRunning = (this.#screenStatus === ScreenStatusType.Running);
    // Prevent running BFCache test on the DevTools window itself via DevTools on DevTools
    const isTestingForbidden = this.#frame.url.startsWith('devtools://');
    // clang-format off
    return LitHtml.html`
      ${this.#renderBackForwardCacheStatus(this.#frame.backForwardCacheDetails.restoredFromCache)}
      <div class='report-line'>
        <div class='report-key'>
          ${i18nString(UIStrings.url)}
        </div>
        <div class='report-value'>
          ${this.#frame.url}
        </div>
      </div>
      ${this.#maybeRenderFrameTree(this.#frame.backForwardCacheDetails.explanationsTree)}
      <${ReportView.ReportView.ReportSection.litTagName}>
        <${Buttons.Button.Button.litTagName}
          .disabled=${isTestRunning || isTestingForbidden}
          .spinner=${isTestRunning}
          .variant=${Buttons.Button.Variant.PRIMARY}
          @click=${this.#navigateAwayAndBack}>
          ${isTestRunning ? LitHtml.html`
            ${i18nString(UIStrings.runningTest)}`:`
            ${i18nString(UIStrings.runTest)}
          `}
        </${Buttons.Button.Button.litTagName}>
      </${ReportView.ReportView.ReportSection.litTagName}>
      <${ReportView.ReportView.ReportSectionDivider.litTagName}>
      </${ReportView.ReportView.ReportSectionDivider.litTagName}>
      ${this.#maybeRenderExplanations(this.#frame.backForwardCacheDetails.explanations,
          this.#frame.backForwardCacheDetails.explanationsTree)}
      <${ReportView.ReportView.ReportSection.litTagName}>
        <x-link href="https://web.dev/bfcache/" class="link">
          ${i18nString(UIStrings.learnMore)}
        </x-link>
      </${ReportView.ReportView.ReportSection.litTagName}>
    `;
    // clang-format on
  }

  #maybeRenderFrameTree(explanationTree: Protocol.Page.BackForwardCacheNotRestoredExplanationTree|
                        undefined): LitHtml.LitTemplate {
    if (!explanationTree || (explanationTree.explanations.length === 0 && explanationTree.children.length === 0) ||
        !Root.Runtime.experiments.isEnabled('bfcacheDisplayTree')) {
      return LitHtml.nothing;
    }
    const treeOutline = new UI.TreeOutline.TreeOutlineInShadow();
    treeOutline.registerCSSFiles([backForwardCacheViewStyles]);
    const urlTreeElement = new UI.TreeOutline.TreeElement();
    treeOutline.appendChild(urlTreeElement);
    const {frameCount, issueCount} = this.#maybeAddFrameSubTree(urlTreeElement, {blankCount: 1}, explanationTree);
    // The translation pipeline does not support nested plurals. We avoid this
    // here by pulling out the logic for one of the plurals into code instead.
    if (frameCount === 1) {
      urlTreeElement.title = i18nString(UIStrings.issuesInSingleFrame, {n: issueCount});
    } else {
      urlTreeElement.title = i18nString(UIStrings.issuesInMultipleFrames, {n: issueCount, m: frameCount});
    }
    // The first element is always the root, so expand it by default (and override its icon).
    const topFrameElement = urlTreeElement.childAt(0);
    if (topFrameElement) {
      topFrameElement.expand();
      topFrameElement.setLeadingIcons([UI.Icon.Icon.create('mediumicon-frame')]);
    }
    return LitHtml.html`
    <div class='report-line'>
    <div class='report-key'>
      ${i18nString(UIStrings.framesTitle)}
    </div>
    <div class='report-value'>
      ${treeOutline.element}
    </div>
  </div>`;
  }

  // Potentially adds a subtree of the frame tree, if there are any issues. Returns a tuple of how many frames were added,
  // and how many issues there were in total over all those frames.
  #maybeAddFrameSubTree(
      root: UI.TreeOutline.TreeElement, nextBlankURLCount: {blankCount: number},
      explanationTree: Protocol.Page.BackForwardCacheNotRestoredExplanationTree|
      undefined): {frameCount: number, issueCount: number} {
    if (!explanationTree || (explanationTree.explanations.length === 0 && explanationTree.children.length === 0)) {
      return {frameCount: 0, issueCount: 0};
    }
    const icon = UI.Icon.Icon.create('mediumicon-frame-embedded');
    let issuecount = explanationTree.explanations.length;
    let framecount = 0;
    let treeElementURL: string;
    if (explanationTree.url.length > 0) {
      treeElementURL = explanationTree.url;
    } else {
      treeElementURL = i18nString(UIStrings.blankURLTitle, {PH1: String(nextBlankURLCount.blankCount)});
      nextBlankURLCount.blankCount += 1;
    }
    const urlTreeElement = new UI.TreeOutline.TreeElement();
    root.appendChild(urlTreeElement);
    urlTreeElement.setLeadingIcons([icon]);
    explanationTree.explanations.forEach(explanation => {
      urlTreeElement.appendChild(new UI.TreeOutline.TreeElement(explanation.reason));
    });
    explanationTree.children.forEach(child => {
      const counts = this.#maybeAddFrameSubTree(urlTreeElement, nextBlankURLCount, child);
      framecount += counts.frameCount;
      issuecount += counts.issueCount;
    });
    if (issuecount > 0) {
      urlTreeElement.title = '(' + String(issuecount) + ') ' + treeElementURL;
      framecount += 1;
    } else if (framecount === 0) {
      root.removeChild(urlTreeElement);
    }

    return {frameCount: framecount, issueCount: issuecount};
  }

  #renderBackForwardCacheStatus(status: boolean|undefined): LitHtml.TemplateResult {
    switch (status) {
      case true:
        // clang-format off
        return LitHtml.html`
          <${ReportView.ReportView.ReportSection.litTagName}>
            <div class='status'>
              <${IconButton.Icon.Icon.litTagName} class="inline-icon" .data=${{
                iconName: 'ic_checkmark_16x16',
                color: 'green',
                width: '16px',
                height: '16px',
                } as IconButton.Icon.IconData}>
              </${IconButton.Icon.Icon.litTagName}>
            </div>
            ${i18nString(UIStrings.restoredFromBFCache)}
          </${ReportView.ReportView.ReportSection.litTagName}>
        `;
        // clang-format on
      case false:
        // clang-format off
        return LitHtml.html`
          <${ReportView.ReportView.ReportSection.litTagName}>
            <div class='status'>
              <${IconButton.Icon.Icon.litTagName} class="inline-icon" .data=${{
                  iconName: 'circled_backslash_icon',
                  color: 'var(--color-text-secondary)',
                  width: '16px',
                  height: '16px',
                  } as IconButton.Icon.IconData}>
              </${IconButton.Icon.Icon.litTagName}>
            </div>
            ${i18nString(UIStrings.normalNavigation)}
          </${ReportView.ReportView.ReportSection.litTagName}>
        `;
        // clang-format on
    }
    // clang-format off
    return LitHtml.html`
    <${ReportView.ReportView.ReportSection.litTagName}>
      ${i18nString(UIStrings.unknown)}
    </${ReportView.ReportView.ReportSection.litTagName}>
    `;
    // clang-format on
  }

  #buildReasonToFramesMap(
      explanationTree: Protocol.Page.BackForwardCacheNotRestoredExplanationTree,
      nextBlankURLCount: {blankCount: number},
      outputMap: Map<Protocol.Page.BackForwardCacheNotRestoredReason, string[]>): void {
    let url = explanationTree.url;
    if (url.length === 0) {
      url = i18nString(UIStrings.blankURLTitle, {PH1: String(nextBlankURLCount.blankCount)});
      nextBlankURLCount.blankCount += 1;
    }
    explanationTree.explanations.forEach(explanation => {
      let frames: string[]|undefined = outputMap.get(explanation.reason);
      if (frames === undefined) {
        frames = [url];
        outputMap.set(explanation.reason, frames);
      } else {
        frames.push(url);
      }
    });
    explanationTree.children.map(child => {
      this.#buildReasonToFramesMap(child, nextBlankURLCount, outputMap);
    });
  }

  #maybeRenderExplanations(
      explanations: Protocol.Page.BackForwardCacheNotRestoredExplanation[],
      explanationTree: Protocol.Page.BackForwardCacheNotRestoredExplanationTree|undefined): LitHtml.LitTemplate {
    if (explanations.length === 0) {
      return LitHtml.nothing;
    }

    const pageSupportNeeded = explanations.filter(
        explanation => explanation.type === Protocol.Page.BackForwardCacheNotRestoredReasonType.PageSupportNeeded);
    const supportPending = explanations.filter(
        explanation => explanation.type === Protocol.Page.BackForwardCacheNotRestoredReasonType.SupportPending);
    const circumstantial = explanations.filter(
        explanation => explanation.type === Protocol.Page.BackForwardCacheNotRestoredReasonType.Circumstantial);

    const reasonToFramesMap: Map<Protocol.Page.BackForwardCacheNotRestoredReason, string[]> = new Map();
    if (explanationTree) {
      this.#buildReasonToFramesMap(explanationTree, {blankCount: 1}, reasonToFramesMap);
    }
    // Disabled until https://crbug.com/1079231 is fixed.
    // clang-format off
    return LitHtml.html`
      ${this.#renderExplanations(i18nString(UIStrings.pageSupportNeeded), i18nString(UIStrings.pageSupportNeededExplanation), pageSupportNeeded, reasonToFramesMap)}
      ${this.#renderExplanations(i18nString(UIStrings.supportPending), i18nString(UIStrings.supportPendingExplanation), supportPending, reasonToFramesMap)}
      ${this.#renderExplanations(i18nString(UIStrings.circumstantial), i18nString(UIStrings.circumstantialExplanation), circumstantial, reasonToFramesMap)}
    `;
    // clang-format on
  }

  #renderExplanations(
      category: Platform.UIString.LocalizedString, explainerText: Platform.UIString.LocalizedString,
      explanations: Protocol.Page.BackForwardCacheNotRestoredExplanation[],
      reasonToFramesMap: Map<Protocol.Page.BackForwardCacheNotRestoredReason, string[]>): LitHtml.TemplateResult {
    // Disabled until https://crbug.com/1079231 is fixed.
    // clang-format off
    return LitHtml.html`
      ${explanations.length > 0 ? LitHtml.html`
        <${ReportView.ReportView.ReportSectionHeader.litTagName}>
          ${category}
          <div class='help-outline-icon'>
            <${IconButton.Icon.Icon.litTagName} class="inline-icon" .data=${{
              iconName: 'help_outline',
              color: 'var(--color-text-secondary)',
              width: '16px',
              height: '16px',
              } as IconButton.Icon.IconData} title=${explainerText}>
            </${IconButton.Icon.Icon.litTagName}>
          </div>
        </${ReportView.ReportView.ReportSectionHeader.litTagName}>
        ${explanations.map(explanation => this.#renderReason(explanation, reasonToFramesMap.get(explanation.reason)))}
      ` : LitHtml.nothing}
    `;
    // clang-format on
  }

  #maybeRenderReasonContext(explanation: Protocol.Page.BackForwardCacheNotRestoredExplanation): LitHtml.LitTemplate {
    if (explanation.reason ===
            Protocol.Page.BackForwardCacheNotRestoredReason.EmbedderExtensionSentMessageToCachedFrame &&
        explanation.context) {
      const link = 'chrome://extensions/?id=' + explanation.context;
      // clang-format off
    return LitHtml.html`${i18nString(UIStrings.blockingExtensionId)}
      <${ChromeLink.ChromeLink.ChromeLink.litTagName} .href=${link}>${explanation.context}</${ChromeLink.ChromeLink.ChromeLink.litTagName}>`;
      // clang-format on
    }
    return LitHtml.nothing;
  }

  #renderFramesPerReason(frames: string[]|undefined): LitHtml.LitTemplate {
    if (frames === undefined || frames.length === 0 || !Root.Runtime.experiments.isEnabled('bfcacheDisplayTree')) {
      return LitHtml.nothing;
    }
    const rows = [LitHtml.html`<div>${i18nString(UIStrings.framesPerIssue, {n: frames.length})}</div>`];
    rows.push(...frames.map(url => LitHtml.html`<div class='text-ellipsis' title=${url}>${url}</div>`));
    return LitHtml.html`
      <div class='explanation-frames'>
        <${ExpandableList.ExpandableList.ExpandableList.litTagName} .data=${
        {rows} as
        ExpandableList.ExpandableList.ExpandableListData}></${ExpandableList.ExpandableList.ExpandableList.litTagName}>
      </div>
    `;
  }

  #maybeRenderDeepLinkToUnload(explanation: Protocol.Page.BackForwardCacheNotRestoredExplanation): LitHtml.LitTemplate {
    if (explanation.reason === Protocol.Page.BackForwardCacheNotRestoredReason.UnloadHandlerExistsInMainFrame ||
        explanation.reason === Protocol.Page.BackForwardCacheNotRestoredReason.UnloadHandlerExistsInSubFrame) {
      return LitHtml.html`
        <x-link href="https://web.dev/bfcache/#never-use-the-unload-event" class="link">
          ${i18nString(UIStrings.neverUseUnload)}
        </x-link>`;
    }
    return LitHtml.nothing;
  }

  #renderReason(explanation: Protocol.Page.BackForwardCacheNotRestoredExplanation, frames: string[]|undefined):
      LitHtml.TemplateResult {
    // clang-format off
    return LitHtml.html`
      <${ReportView.ReportView.ReportSection.litTagName}>
        ${(explanation.reason in NotRestoredReasonDescription) ?
          LitHtml.html`
            <div class='circled-exclamation-icon'>
              <${IconButton.Icon.Icon.litTagName} class="inline-icon" .data=${{
                iconName: 'circled_exclamation_icon',
                color: 'orange',
                width: '16px',
                height: '16px',
              } as IconButton.Icon.IconData}>
              </${IconButton.Icon.Icon.litTagName}>
            </div>
            <div>
              ${NotRestoredReasonDescription[explanation.reason].name()}
              ${this.#maybeRenderDeepLinkToUnload(explanation)}
             ${this.#maybeRenderReasonContext(explanation)}
           </div>` :
            LitHtml.nothing}
      </${ReportView.ReportView.ReportSection.litTagName}>
      <div class='gray-text'>
        ${explanation.reason}
      </div>
      ${this.#renderFramesPerReason(frames)}
    `;
    // clang-format on
  }
}

ComponentHelpers.CustomElements.defineComponent('devtools-resources-back-forward-cache-view', BackForwardCacheView);

declare global {
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  interface HTMLElementTagNameMap {
    'devtools-resources-back-forward-cache-view': BackForwardCacheView;
  }
}
