import { html, TemplateResult } from 'lit';
import { AmfMixin, AmfNamespace, IApplication } from '@api-client/core/build/browser.js';
import { AmfDemoPage } from './lib/AmfDemoPage.js';
import { reactive } from '../../src/lib/decorators.js';
import { ApiNavigationEvent } from '../../src/index.js';
import { ServerType } from '../../src/amf/types.js';
import '../../src/define/amf/api-operation-document.js';
import '../../src/define/amf/api-request.js';
import '../../src/define/amf/api-server-picker.js';
import '../../src/define/ui/ui-checkbox.js';
import '../../src/define/ui/ui-dialog.js';
import '../../src/define/ui/ui-button.js';
import { WebProxyBindings } from '../../src/bindings/web/WebProxyBindings.js';
import env from '../env.js';

const AppInfo: IApplication = { code: 'demo', name: 'Demo', 'version': '0' };

class AmfHelper extends AmfMixin(Object) {}

class ComponentPage extends AmfDemoPage {
  @reactive() selectedId?: string;

  @reactive() selectedType?: string;
  
  @reactive() endpointId?: string;

  @reactive() editorOperation?: string;

  @reactive() serverType?: string;

  @reactive() serverValue?: string;

  @reactive() tryIt: boolean;

  @reactive() overrideBaseUri: boolean;

  @reactive() renderSecurity: boolean;

  @reactive() renderCodeSnippets: boolean;

  @reactive() editorOpened?: boolean;

  http = new WebProxyBindings(AppInfo, `${env.proxyUri}/open`);

  constructor() {
    super();
    
    this.tryIt = true;
    this.overrideBaseUri = false;
    this.renderSecurity = true;
    this.renderCodeSnippets = true;
    this.componentName = 'api-operation-document';
    this.redirectUri = `${window.location.origin}/demo/oauth-popup.html`;
    [
      ['httpbin', 'httpbin.org'],
      ['google-drive-api', 'Google Drive'],
      ['multi-server', 'Multiple servers'],
      ['nexmo-sms-api', 'Nexmo SMS API'],
      ['appian-api', 'Applian API'],
      ['async-api', 'Async API'],
      ['Petstore-v2', 'Petstore OAS API'],
      ['api-keys', 'API key (OAS)'],
      ['oauth-flows', 'OAuth 2 flows'],
      ['oas-bearer', 'Bearer token'],
      ['oauth-pkce', 'OAuth 2 PKCE'],
      ['secured-unions', 'Secured unions'],
      ['secured-api', 'Secured API'],
      ['oas-callbacks', 'OAS 3 callbacks'],
      ['APIC-15', 'APIC-15'],
      ['APIC-332', 'APIC-332'],
      ['APIC-463', 'APIC-463'],
      ['APIC-553', 'APIC-553'],
      ['APIC-560', 'APIC-560'],
      ['APIC-582', 'APIC-582'],
      ['APIC-650', 'APIC-650'],
      ['anyOf', 'APIC-561'],
      ['SE-10469', 'SE-10469'],
      ['SE-11508', 'SE-11508'],
      ['SE-12957', 'SE-12957: OAS query parameters documentation'],
      ['SE-11415', 'SE-11415'],
      ['SE-12752', 'SE-12752: Query string'],
      ['SE-12957', 'SE-12957'],
      ['SE-12959', 'SE-12959: OAS summary field'],
    ].forEach(([file, label]) => {
      this.apis.push({
        file: `models/${file}.json`,
        label,
      })
    });
    this.autoLoad();
  }

  override async autoLoad(): Promise<void> {
    await this.http.initialize();
    return super.autoLoad();
  }

  get baseUri(): string | undefined {
    const { serverValue, serverType } = this;
    if (['custom', 'uri'].includes(serverType as string)) {
      return serverValue;
    }
    return undefined;
  }

  get serverId(): string | undefined {
    const { serverValue, serverType, endpointId, selectedId } = this;
    if (!serverValue || ['custom', 'uri'].includes(serverType as string)) {
      return undefined;
    }
    const helper = new AmfHelper();
    helper.amf = this.amf;

    const servers = helper._getServers({ endpointId, methodId: selectedId });
    if (!Array.isArray(servers)) {
      return undefined;
    }
    const srv = servers.find((item) => {
      const url = helper._getValue(item, AmfNamespace.aml.vocabularies.core.urlTemplate) as string;
      return url === serverValue;
    });
    if (srv) {
      return srv['@id'];
    }
    return undefined;
  }

  _serverHandler(e: CustomEvent): void {
    const { value, type } = e.detail;
    this.serverType = type;
    this.serverValue = value;
  }


  _navChanged(e: ApiNavigationEvent): void {
    const { domainId, domainType, passive } = e.detail;
    if (passive) {
      return;
    }
    if (domainType === 'operation') {
      this.selectedId = domainId;
      this.selectedType = domainType;
    } else {
      this.selectedId = undefined;
      this.selectedType = undefined;
      this.endpointId = undefined;
    }
  }

  tryitHandler(e: CustomEvent): void {
    const { id } = e.detail;
    this.editorOperation = id;
    this.editorOpened = true;
  }

  editorCloseHandler(): void {
    this.editorOperation = undefined;
    this.editorOpened = false;
  }

  contentTemplate(): TemplateResult {
    return html`
      <a href="./">Back</a>
      ${this.demoTemplate()}
    `;
  }

  demoTemplate(): TemplateResult {
    const { loaded } = this;
    return html`
      ${!loaded ? html`<p>Load an API model first.</p>` : this.loadedTemplate()}
    `;
  }

  loadedTemplate(): TemplateResult {
    return html`
    ${this.componentTemplate()}
    ${this.requestEditorDialogTemplate()}
    `;
  }

  componentTemplate(): TemplateResult {
    const { 
        selectedId, tryIt, overrideBaseUri, baseUri, serverId,
        renderSecurity, renderCodeSnippets,
    } = this;
    if (!selectedId) {
      return html`<p>Select API operation in the navigation</p>`;
    }
    let finalBaseUri: string | undefined;
    if (baseUri) {
      finalBaseUri = baseUri;
    } else if (overrideBaseUri) {
      finalBaseUri = 'https://custom.api.com';
    }
    return html`
    <section class="demo-section">
      <api-operation-document
        class="operation-surface surface1"
        .domainId="${selectedId}"
        .baseUri="${finalBaseUri}"
        .serverId="${serverId}"
        ?tryItButton="${tryIt}"
        ?renderCodeSnippets="${renderCodeSnippets}"
        ?showSecurity="${renderSecurity}"
        @tryit="${this.tryitHandler}"
      >
      </api-operation-document>
    </section>

    <section class="demo-section">
      <p class="block" id="mainOptionsLabel">Options</p>
      <label class="label-large">
        <ui-checkbox
          @change="${this._toggleBooleanOption}" 
          ?checked="${tryIt}"
          name="tryIt"
          aria-describedby="mainOptionsLabel"
        ></ui-checkbox>
        Render try it
      </label>

      <label class="label-large">
        <ui-checkbox
          @change="${this._toggleBooleanOption}" 
          ?checked="${overrideBaseUri}"
          name="overrideBaseUri"
          aria-describedby="mainOptionsLabel"
        ></ui-checkbox>
        Custom base URI
      </label>

      <label class="label-large">
        <ui-checkbox
          @change="${this._toggleBooleanOption}" 
          ?checked="${renderSecurity}"
          name="renderSecurity"
          aria-describedby="mainOptionsLabel"
        ></ui-checkbox>
        Render security
      </label>

      <label class="label-large">
        <ui-checkbox
          @change="${this._toggleBooleanOption}" 
          ?checked="${renderCodeSnippets}"
          name="renderCodeSnippets"
          aria-describedby="mainOptionsLabel"
        ></ui-checkbox>
        Render code snippets
      </label>
    </section>
    `;
  }

  requestEditorDialogTemplate(): TemplateResult {
    return html`
    <ui-dialog modal @close="${this.editorCloseHandler}" .open="${this.editorOpened || false}" class="request-dialog">
      <span slot="title">API request</span>
      <api-request
        .domainId="${this.editorOperation}"
        urlLabel
        allowHideOptional
        .redirectUri="${this.redirectUri}"
      >
      </api-request>
      <ui-button slot="button" value="dismiss" type="text">Close</ui-button>
    </ui-dialog>
    `;
  }

  /**
   * @returns A template for the server selector
   */
  serverSelectorTemplate(): TemplateResult {
    const {
      serverType,
      serverValue,
    } = this;
    return html`
    <api-server-picker
      .value="${serverValue}"
      .type="${serverType as ServerType}"
      auto
      custom
      @apiserverchanged="${this._serverHandler}"
    ></api-server-picker>`;
  }
}
const instance = new ComponentPage();
instance.render();
