// Copyright 2023 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 * as Protocol from '../../../../generated/protocol.js';
import {
  renderElementIntoDOM,
} from '../../../../testing/DOMHelpers.js';
import {describeWithEnvironment} from '../../../../testing/EnvironmentHelpers.js';
import * as RenderCoordinator from '../../../../ui/components/render_coordinator/render_coordinator.js';
import type * as TextEditor from '../../../../ui/components/text_editor/text_editor.js';

import * as PreloadingComponents from './components.js';

async function renderRuleSetDetailsView(
    data: PreloadingComponents.RuleSetDetailsView.RuleSetDetailsViewData,
    shouldPrettyPrint: boolean): Promise<HTMLElement> {
  const component = new PreloadingComponents.RuleSetDetailsView.RuleSetDetailsView();
  component.shouldPrettyPrint = shouldPrettyPrint;
  component.data = data;
  renderElementIntoDOM(component);
  assert.isNotNull(component.shadowRoot);
  await RenderCoordinator.done();

  return component;
}

describeWithEnvironment('RuleSetDetailsView', () => {
  it('renders nothing if not selected', async () => {
    const data = null;

    const component = await renderRuleSetDetailsView(data, false);
    assert.isNotNull(component.shadowRoot);
    assert.strictEqual(component.shadowRoot.textContent, '');
  });

  it('renders rule set', async () => {
    const data: Protocol.Preload.RuleSet = {
      id: 'ruleSetId:1' as Protocol.Preload.RuleSetId,
      loaderId: 'loaderId:1' as Protocol.Network.LoaderId,
      sourceText: `
{
  "prefetch": [
    {
      "source": "list",
      "urls": ["/subresource.js"]
    }
  ]
}
`,
      backendNodeId: 1 as Protocol.DOM.BackendNodeId,
    };
    const component = await renderRuleSetDetailsView(data, false);
    assert.isUndefined(component.shadowRoot?.getElementById('error-message-text')?.textContent);

    const textEditor = component.shadowRoot?.querySelector('devtools-text-editor') as TextEditor.TextEditor.TextEditor;
    assert.strictEqual(textEditor.state.doc.toString(), data.sourceText);
  });

  it('renders rule set from Speculation-Rules HTTP header', async () => {
    const data: Protocol.Preload.RuleSet = {
      id: 'ruleSetId:1' as Protocol.Preload.RuleSetId,
      loaderId: 'loaderId:1' as Protocol.Network.LoaderId,
      sourceText: `
{
  "prefetch": [
    {
      "source": "list",
      "urls": ["/subresource.js"]
    }
  ]
}
`,
      url: 'https://example.com/speculationrules.json',
      requestId: 'reqeustId' as Protocol.Network.RequestId,
    };
    const component = await renderRuleSetDetailsView(data, false);
    assert.isUndefined(component.shadowRoot?.getElementById('error-message-text')?.textContent);
    const textEditor = component.shadowRoot?.querySelector('devtools-text-editor') as TextEditor.TextEditor.TextEditor;
    assert.strictEqual(textEditor.state.doc.toString(), data.sourceText);
  });

  it('renders invalid rule set, broken JSON', async () => {
    const data: Protocol.Preload.RuleSet = {
      id: 'ruleSetId:1' as Protocol.Preload.RuleSetId,
      loaderId: 'loaderId:1' as Protocol.Network.LoaderId,
      sourceText: `
{
  "prefetch": [
    {
      "source": "list",
`,
      backendNodeId: 1 as Protocol.DOM.BackendNodeId,
      errorType: Protocol.Preload.RuleSetErrorType.SourceIsNotJsonObject,
      errorMessage: 'Line: 6, column: 1, Syntax error.',
    };
    const component = await renderRuleSetDetailsView(data, false);
    assert.deepEqual(
        component.shadowRoot?.getElementById('error-message-text')?.textContent, 'Line: 6, column: 1, Syntax error.');
    const textEditor = component.shadowRoot?.querySelector('devtools-text-editor') as TextEditor.TextEditor.TextEditor;
    assert.strictEqual(textEditor.state.doc.toString(), data.sourceText);
  });

  it('renders invalid rule set, lacking `urls`', async () => {
    const data: Protocol.Preload.RuleSet = {
      id: 'ruleSetId:1' as Protocol.Preload.RuleSetId,
      loaderId: 'loaderId:1' as Protocol.Network.LoaderId,
      sourceText: `
{
  "prefetch": [
    {
      "source": "list"
    }
  ]
}
`,
      backendNodeId: 1 as Protocol.DOM.BackendNodeId,
      errorType: Protocol.Preload.RuleSetErrorType.InvalidRulesSkipped,
      errorMessage: 'A list rule must have a "urls" array.',
    };
    const component = await renderRuleSetDetailsView(data, false);
    assert.deepEqual(
        component.shadowRoot?.getElementById('error-message-text')?.textContent,
        'A list rule must have a "urls" array.');
    const textEditor = component.shadowRoot?.querySelector('devtools-text-editor') as TextEditor.TextEditor.TextEditor;
    assert.strictEqual(textEditor.state.doc.toString(), data.sourceText);
  });

  it('renders formatted rule set', async () => {
    const data: Protocol.Preload.RuleSet = {
      id: 'ruleSetId:1' as Protocol.Preload.RuleSetId,
      loaderId: 'loaderId:1' as Protocol.Network.LoaderId,
      sourceText: '{"prefetch":[{"source": "list","urls": ["/subresource.js"]}]}',
      backendNodeId: 1 as Protocol.DOM.BackendNodeId,
    };
    const component = await renderRuleSetDetailsView(data, true);
    assert.isUndefined(component.shadowRoot?.getElementById('error-message-text')?.textContent);

    const textEditor = component.shadowRoot?.querySelector('devtools-text-editor') as TextEditor.TextEditor.TextEditor;
    // Formatted sourceText should be different from the data.sourceText in this case.
    assert.notEqual(textEditor.state.doc.toString(), data.sourceText);
    assert.strictEqual(textEditor.state.doc.toString(), `{
    "prefetch": [
        {
            "source": "list",
            "urls": [
                "/subresource.js"
            ]
        }
    ]
}`);
  });
});
