import { fixture, assert, aTimeout, nextFrame, html } from '@open-wc/testing';
import sinon from 'sinon';
import { AmfDefinitions, ApiDefinitions, IRamlCustomAuthorization } from '@api-client/core/build/browser.js';
import { AmfLoader } from "../AmfLoader.js";
import AmfRamlCustom from '../../../src/elements/amf/authorization/AmfRamlCustom.js';
import Input from '../../../src/ui/input/Input.js';
import '../../../src/define/amf/authorization/amf-ramlcustom-authorization.js';

describe.skip('RAML custom scheme', () => {
  async function methodFixture(security?: ApiDefinitions.IApiParametrizedSecurityScheme): Promise<AmfRamlCustom> {
    return (fixture(html`<amf-ramlcustom-authorization .security="${security}"></amf-ramlcustom-authorization>`));
  }

  let store: AmfLoader;
  let model: AmfDefinitions.IAmfDocument;
  before(async () => {
    store = new AmfLoader();
  });

  function getApiParametrizedSecurityScheme(path: string, method: string): ApiDefinitions.IApiParametrizedSecurityScheme {
    const operation = store.getOperation(model, path, method);
    return operation.security[0].schemes[0];
  }

  describe('Secured API tests', () => {
    before(async () => {
      model = await store.getGraph('secured-api');
    });

    describe('initialization', () => {
      it('can be initialized with document.createElement', () => {
        const element = document.createElement('amf-ramlcustom-authorization');
        assert.ok(element);
      });

      it('can be initialized in a template with model', async () => {
        const operation = store.getOperation(model, '/custom2', 'get');
        const element = await methodFixture(operation.security[0]);
        await aTimeout(0);
        assert.ok(element);
      });
    });
  
    describe('content rendering', () => {
      it('renders headers', async () => {
        const scheme = getApiParametrizedSecurityScheme('/custom1', 'get');
        const element = await methodFixture(scheme);
        const nodes = element.shadowRoot!.querySelectorAll(`[data-binding="header"]`);
        assert.lengthOf(nodes, 1);
      });
  
      it('renders query parameters', async () => {
        const scheme = getApiParametrizedSecurityScheme('/custom2', 'get');
        const element = await methodFixture(scheme);
        const nodes = element.shadowRoot!.querySelectorAll(`[data-binding="query"]`);
        assert.lengthOf(nodes, 2);
      });
  
      it('renders scheme title', async () => {
        const scheme = getApiParametrizedSecurityScheme('/custom1', 'get');
        const element = await methodFixture(scheme);
        const node = element.shadowRoot!.querySelector(`.subtitle`) as HTMLElement;
        const result = (node.textContent as string).trim();
        assert.equal(result, 'Scheme: custom1');
      });
  
      it('renders scheme desc toggle button', async () => {
        const scheme = getApiParametrizedSecurityScheme('/custom1', 'get');
        const element = await methodFixture(scheme);
        const node = element.shadowRoot!.querySelector(`.subtitle .hint-icon`);
        assert.ok(node);
      });
  
      it('ignores other security schemes', async () => {
        const scheme = getApiParametrizedSecurityScheme('/basic', 'get');
        const element = await methodFixture(scheme);
        const nodes = element.shadowRoot!.querySelectorAll(`[data-binding="header"]`);
        assert.lengthOf(nodes, 0);
      });
    });
  
    describe('description rendering', () => {
      it('does not render scheme description by default', async () => {
        const scheme = getApiParametrizedSecurityScheme('/custom1', 'get');
        const element = await methodFixture(scheme);
        const node = element.shadowRoot!.querySelector(`.subtitle`) as HTMLElement;
        const next = node.nextElementSibling as HTMLElement;
        assert.equal(next.localName, 'form');
      });
  
      it('renders scheme description after activation', async () => {
        const scheme = getApiParametrizedSecurityScheme('/custom1', 'get');
        const element = await methodFixture(scheme);
        const button = element.shadowRoot!.querySelector(`.subtitle .hint-icon`) as HTMLElement;
        (button as HTMLElement).click();
        await nextFrame();
        const node = element.shadowRoot!.querySelector(`.subtitle`) as HTMLElement;
        const next = node.nextElementSibling as HTMLElement;
        assert.isTrue(next.classList.contains('docs-container'));
      });
    });
  
    describe('change notification', () => {
      it('notifies when value change', async () => {
        const scheme = getApiParametrizedSecurityScheme('/custom1', 'get');
        const element = await methodFixture(scheme);
        const input = (element.shadowRoot!.querySelector(`[name="SpecialTokenHeader"]`)) as Input;
        input.value = 'test';
        const spy = sinon.spy();
        element.addEventListener('change', spy);
        input.dispatchEvent(new CustomEvent('change'));
        await nextFrame();
        assert.isTrue(spy.called);
      });
  
      it('notifies when selection change', async () => {
        const scheme = getApiParametrizedSecurityScheme('/custom1', 'get');
        const element = await methodFixture(scheme);
        const input = element.shadowRoot!.querySelector(`[name="debugTokenParam"]`) as Input;
        const option = input.querySelector(`[data-value="Log"]`) as HTMLElement;
        const spy = sinon.spy();
        element.addEventListener('change', spy);
        (option as HTMLElement).click();
        await nextFrame();
        assert.isTrue(spy.called);
      });
    });
  
    describe('updateQueryParameter()', () => {
      it('updates query parameter value', async () => {
        const scheme = getApiParametrizedSecurityScheme('/custom1', 'get');
        const element = await methodFixture(scheme);
        element.updateQueryParameter('debugTokenParam', 'Log');
        const result = element.serialize() as IRamlCustomAuthorization;
        assert.equal((result.query as Record<string, string>).debugTokenParam, 'Log');
      });
  
      it('updates boolean value', async () => {
        const scheme = getApiParametrizedSecurityScheme('/custom1', 'get');
        const element = await methodFixture(scheme);
        element.updateQueryParameter('booleanTokenParam', 'false');
        const result = element.serialize() as IRamlCustomAuthorization;
        assert.equal((result.query as Record<string, string>).booleanTokenParam, 'false');
      });
  
      it('updates string value', async () => {
        const scheme = getApiParametrizedSecurityScheme('/custom2', 'get');
        const element = await methodFixture(scheme);
        element.updateQueryParameter('apiNonceParam', 'test');
        const result = element.serialize() as IRamlCustomAuthorization;
        assert.equal((result.query as Record<string, string>).apiNonceParam, 'test');
      });
    });
  
    describe('updateHeader()', () => {
      it('updates header value', async () => {
        const scheme = getApiParametrizedSecurityScheme('/custom1', 'get');
        const element = await methodFixture(scheme);
        element.updateHeader('SpecialTokenHeader', 'testHeader');
        const result = element.serialize() as IRamlCustomAuthorization;
        assert.equal((result.header as Record<string, string>).SpecialTokenHeader, 'testHeader');
      });
    });
  
    describe('Support for queryString property', () => {
      let element: AmfRamlCustom;
  
      beforeEach(async () => {
        const scheme = getApiParametrizedSecurityScheme('/custom3', 'get');
        element = await methodFixture(scheme);
      });
  
      it('renders input filled for NodeShape', () => {
        const nodes = element.shadowRoot!.querySelectorAll(`[data-binding="query"]`);
        assert.lengthOf(nodes, 2);
      });
    });
  
    describe('checkValidity()', () => {
      let element: AmfRamlCustom;
  
      beforeEach(async () => {
        const scheme = getApiParametrizedSecurityScheme('/custom3', 'get');
        element = await methodFixture(scheme);
      });
  
      it('returns false when required field is empty', () => {
        const result = element.checkValidity();
        assert.isFalse(result);
      });
  
      it('renders optional field valid', async () => {
        element.checkValidity();
        await nextFrame();
        const input = (element.shadowRoot!.querySelector(`[name="queryStringProperty2"]`)) as Input;
        assert.isUndefined(input.invalid);
      });
  
      it('returns true when valid', async () => {
        const input = (element.shadowRoot!.querySelector(`[name="queryStringProperty1"]`)) as Input;
        input.value = '123';
        input.dispatchEvent(new CustomEvent('change'));
        await nextFrame();
        const result = element.checkValidity();
        assert.isTrue(result);
      });
    });
  
    describe('a11y', () => {
      it('is accessible for custom fields (headers and qp)', async () => {
        const scheme = getApiParametrizedSecurityScheme('/custom1', 'get');
        const element = await methodFixture(scheme);
        await assert.isAccessible(element);
      });
    });
  });

  
  describe('Event-based population', () => {
    let element: AmfRamlCustom;

    before(async () => {
      model = await store.getGraph();
    });

    beforeEach(async () => {
      const security = getApiParametrizedSecurityScheme('/authorization/custom1', 'get');
      element = await methodFixture(security);
    });

    it('populates the header field (compatibility v0.2.0)', async () => {
      const settings = {
        headers: {
          SpecialTokenHeader: 'test_value',
        },
      }
      element.dispatchEvent(new CustomEvent('securitysettingsinfochanged', { detail: settings }));
      await nextFrame();
      const { header } = element.serialize() as IRamlCustomAuthorization;
      assert.equal((header as Record<string, string>).SpecialTokenHeader, 'test_value');
    });

    it('populates the header field', async () => {
      const settings = {
        header: {
          SpecialTokenHeader: 'test_value',
        },
      }
      element.dispatchEvent(new CustomEvent('securitysettingsinfochanged', { detail: settings }));
      await nextFrame();
      const { header } = element.serialize() as IRamlCustomAuthorization;
      assert.equal((header as Record<string, string>).SpecialTokenHeader, 'test_value');
    });

    it('populates query params fields (compatibility v0.2.0)', async () => {
      const settings = {
        params: {
          debugTokenParam: 'Info',
          booleanTokenParam: true,
        },
      }
      element.dispatchEvent(new CustomEvent('securitysettingsinfochanged', { detail: settings }));
      await nextFrame();
      const { query } = element.serialize() as IRamlCustomAuthorization;
      assert.equal((query as Record<string, string>).debugTokenParam, 'Info');
      assert.equal((query as Record<string, unknown>).booleanTokenParam, true);
    });

    it('populates query params fields', async () => {
      const settings = {
        query: {
          debugTokenParam: 'Info',
          booleanTokenParam: true,
        },
      }
      element.dispatchEvent(new CustomEvent('securitysettingsinfochanged', { detail: settings }));
      await nextFrame();
      const { query } = element.serialize() as IRamlCustomAuthorization;
      assert.equal((query as Record<string, string>).debugTokenParam, 'Info');
      assert.equal((query as Record<string, unknown>).booleanTokenParam, true);
    });

    it('changes only one query params field', async () => {
      const settings = {
        query: {
          debugTokenParam: 'Info',
          booleanTokenParam: false,
        },
      }
      element.dispatchEvent(new CustomEvent('securitysettingsinfochanged', { detail: settings }));
      await nextFrame();
      const updatedSettings = {
        query: {
          debugTokenParam: 'Info_1',
        },
      }
      element.dispatchEvent(new CustomEvent('securitysettingsinfochanged', { detail: updatedSettings }));
      await nextFrame();
      const { query } = element.serialize() as IRamlCustomAuthorization;
      assert.equal((query as Record<string, string>).debugTokenParam, 'Info_1');
      assert.equal((query as Record<string, unknown>).booleanTokenParam, false);
    });
  });
});
