import { __decorate } from "tslib";
import { html } from 'lit';
import { property } from 'lit/decorators.js';
import { AmfNamespace } from '@api-client/core/build/browser.js';
import { ApiDocumentationBase } from './ApiDocumentationBase.js';
import { EventTypes } from '../../events/amf/EventTypes.js';
import { Events } from '../../events/amf/Events.js';
import '../../define/amf/api-summary.js';
import '../../define/amf/api-operation-document.js';
import '../../define/amf/api-resource-document.js';
import '../../define/amf/api-security-document.js';
import '../../define/amf/api-documentation-document.js';
import '../../define/amf/api-schema-document.js';
import '../../define/amf/api-server-picker.js';
import '../../define/ui/ui-radio.js';
/**
 * A main documentation view for an AMF model representing a sync or an async API.
 *
 * This element works with the [AMF](https://github.com/mulesoft/amf) data model.
 */
export default class ApiDocumentation extends ApiDocumentationBase {
    /**
     * The type of the selected domain item.
     * @attribute
     */
    get domainType() {
        return this.domainTypeValue;
    }
    set domainType(value) {
        const old = this.domainTypeValue;
        if (old === value) {
            return;
        }
        this.domainTypeValue = value;
        this.requestUpdate('domainType', old);
        if (value) {
            this.processDebounce();
        }
    }
    /**
     * The domain id of the currently rendered API operation.
     * When selecting an operation the `domainId` is the id if the parent endpoint.
     * @attribute
     */
    get operationId() {
        return this.operationIdValue;
    }
    set operationId(value) {
        const old = this.operationIdValue;
        if (old === value) {
            return;
        }
        this.operationIdValue = value;
        this.requestUpdate('operationId', old);
        if (value) {
            this.processDebounce();
        }
    }
    /**
     * By default application hosting the element must set `domainId` and
     * `domainType` properties. When using `api-navigation` element
     * by setting this property the element listens for navigation events
     * and updates the state
     * @attribute
     */
    get handleNavigationEvents() {
        return this.handleNavigationEventsValue;
    }
    set handleNavigationEvents(value) {
        const old = this.handleNavigationEventsValue;
        if (old === value) {
            return;
        }
        this.handleNavigationEventsValue = value;
        if (value) {
            this.registerNavigationEvents();
        }
        else {
            this.unregisterNavigationEvents();
        }
    }
    /**
     * Former `effectiveBaseUri`.
     * @returns The URI for the API defined by the `baseUri` property or the `serverValue`.
     */
    get apiBaseUri() {
        const { baseUri, serverValue } = this;
        return baseUri || serverValue;
    }
    /**
     * @deprecated Use `apiBaseUri` instead.
     */
    get effectiveBaseUri() {
        return this.apiBaseUri;
    }
    /** The domain type of the rendered view. */
    get renderedView() {
        return this.renderedViewValue;
    }
    get renderSelector() {
        const { domainType, serversCount = 0, allowCustomBaseUri } = this;
        const isOperationOrEndpoint = !!domainType && (['operation', 'resource'].includes(domainType));
        const moreThanOneServer = serversCount >= 2;
        if (isOperationOrEndpoint) {
            return allowCustomBaseUri || moreThanOneServer;
        }
        return false;
    }
    /**
     * This is a computed value from the AMF model.
     * @returns true when whe currently loaded API is an async API.
     */
    get isAsync() {
        const { documentMeta } = this;
        if (!documentMeta) {
            return false;
        }
        return documentMeta.isAsync;
    }
    /**
     * @returns The mime type of the schema that is being rendered.
     */
    get schemaMime() {
        const { schemaMimeType } = this;
        if (schemaMimeType) {
            return schemaMimeType;
        }
        const summary = this.apiSummaryValue;
        if (!summary) {
            return undefined;
        }
        const { accepts = [] } = summary;
        if (!accepts.length) {
            return undefined;
        }
        return accepts[0];
    }
    get documentMeta() {
        return this.documentMetaValue;
    }
    constructor() {
        super();
        this.navigationHandler = this.navigationHandler.bind(this);
        this.navEventsRegistered = false;
    }
    connectedCallback() {
        super.connectedCallback();
        this.processDebounce();
    }
    disconnectedCallback() {
        super.disconnectedCallback();
        if (this.navEventsRegistered) {
            this.unregisterNavigationEvents();
        }
    }
    /**
     * Registers the api navigation event listener handler
     * on the window object.
     */
    registerNavigationEvents() {
        this.navEventsRegistered = true;
        window.addEventListener(EventTypes.Navigation.apiNavigate, this.navigationHandler);
    }
    /**
     * Removes event listener from window object for the API navigation event.
     */
    unregisterNavigationEvents() {
        this.navEventsRegistered = false;
        window.removeEventListener(EventTypes.Navigation.apiNavigate, this.navigationHandler);
    }
    /**
     * Handler for the API navigation event.
     *
     * Note, when the current type is set to `operation` then the `operationId` is
     * set instead of `domainId`, which is set to the parent endpoint id.
     */
    navigationHandler(e) {
        const { domainId, domainType, parentId, passive } = e.detail;
        if (passive === true) {
            return;
        }
        this.domainType = domainType;
        if (domainType === 'operation') {
            this.operationId = domainId;
            this.domainId = parentId;
        }
        else {
            this.operationId = undefined;
            this.domainId = domainId;
        }
        this.processGraph();
    }
    async processGraph() {
        this.schemaMimeType = undefined;
        await this.queryDocumentMeta();
        const { documentMeta } = this;
        if (!documentMeta) {
            this.requestUpdate();
            return;
        }
        if (documentMeta.isApi) {
            await this.queryApiSummary();
            this.processApiSpecSelection();
            return;
        }
        if (documentMeta.isLibrary) {
            this.processLibrarySelection();
            return;
        }
        const { isFragment, types } = documentMeta;
        if (isFragment) {
            let type;
            if (types.includes(AmfNamespace.aml.vocabularies.security.SecuritySchemeFragment)) {
                type = 'security';
            }
            else if (types.includes(AmfNamespace.aml.vocabularies.apiContract.UserDocumentationFragment)) {
                type = 'documentation';
            }
            else if (types.includes(AmfNamespace.aml.vocabularies.shapes.DataTypeFragment)) {
                type = 'schema';
            }
            this.processFragment(type);
            return;
        }
        //
        // partial models
        // 
        if (types.includes(AmfNamespace.aml.vocabularies.apiContract.EndPoint)) {
            this.processEndpointPartial();
            return;
        }
        let type;
        if (types.includes(AmfNamespace.aml.vocabularies.core.CreativeWork)) {
            type = 'documentation';
        }
        else if (types.includes(AmfNamespace.aml.vocabularies.security.SecurityScheme)) {
            type = 'security';
        }
        else if (types.includes(AmfNamespace.w3.shacl.Shape)) {
            type = 'schema';
        }
        this.processPartial(type);
    }
    /**
     * Reads the currently loaded document meta data from the store.
     */
    async queryDocumentMeta() {
        this.documentMetaValue = undefined;
        try {
            const info = await Events.Api.documentMeta(this);
            this.documentMetaValue = info;
        }
        catch (e) {
            const ex = e;
            Events.Telemetry.exception(this, ex.message, false);
            Events.Reporting.error(this, ex, `Unable to query for the document meta data: ${ex.message}`, this.localName);
        }
    }
    /**
     * Reads the API summary.
     * Called only when the currently loaded document is an API.
     */
    async queryApiSummary() {
        try {
            const info = await Events.Api.summary(this);
            this.apiSummaryValue = info;
        }
        catch (e) {
            this.apiSummaryValue = undefined;
            const ex = e;
            Events.Telemetry.exception(this, ex.message, false);
            Events.Reporting.error(this, ex, `Unable to query for the API summary data: ${ex.message}`, this.localName);
        }
    }
    /**
     * Processes selection for the web API data model. It ignores the input if
     * `domainId` or `domainType` is not set.
     */
    processApiSpecSelection() {
        const { domainId, tryItPanel } = this;
        let { domainType } = this;
        if (!domainId || !domainType) {
            // Not all required properties were set.
            return;
        }
        if (domainType === 'operation' && tryItPanel) {
            domainType = 'resource';
        }
        this.renderedViewValue = domainType;
        this.requestUpdate();
    }
    /**
     * Processes selection for a library data model. It ignores the input if
     * `domainId` or `domainType` is not set.
     */
    processLibrarySelection() {
        const { domainId, domainType } = this;
        if (!domainId || !domainType) {
            // Not all required properties were set.
            return;
        }
        this.renderedViewValue = domainType;
        this.requestUpdate();
    }
    /**
     * Processes fragment model and sets current selection and the model.
     *
     * @param domainType The selected domain type.
     */
    processFragment(domainType) {
        this.domainIdValue = this.documentMeta?.encodesId;
        this.renderedViewValue = domainType;
        this.requestUpdate();
    }
    /**
     * Sets the partial model to be rendered.
     *
     * @param domainType The domain type representing the partial model.
     */
    processPartial(domainType) {
        this.renderedViewValue = domainType;
        this.requestUpdate();
    }
    /**
     * Processes endpoint data from partial model definition.
     * It sets models that are used by the docs.
     *
     * If `selected` or `selectedType` is not set then it automatically selects
     */
    processEndpointPartial() {
        const { tryItPanel } = this;
        let { domainType } = this;
        if (!domainType || tryItPanel) {
            domainType = 'resource';
        }
        if (!['operation', 'resource'].includes(domainType)) {
            domainType = 'resource';
        }
        this.renderedViewValue = domainType;
        this.requestUpdate();
    }
    serversCountHandler(e) {
        this.serversCount = e.detail.value;
    }
    serverChangeHandler(e) {
        this.serverValue = e.detail.value;
        this.serverType = e.detail.type;
    }
    mediaTypeSelectHandler(e) {
        const node = e.target;
        const { value } = node;
        this.schemaMimeType = value;
    }
    render() {
        return html `
    ${this.serverSelectorTemplate()}
    ${this.modelTemplate()}`;
    }
    /**
     * @returns The template for the server selector.
     */
    serverSelectorTemplate() {
        if (this.noServerSelector) {
            return '';
        }
        const { serverType, serverValue, allowCustomBaseUri, renderSelector, domainId, domainType } = this;
        const id = domainType === 'operation' ? this.operationId : domainId;
        return html `
      <api-server-picker
        class="server-selector"
        .domainId="${id}"
        .domainType="${domainType}"
        .value="${serverValue}"
        .type="${serverType}"
        ?hidden="${!renderSelector}"
        ?custom="${allowCustomBaseUri}"
        auto
        @serverscountchanged="${this.serversCountHandler}"
        @apiserverchanged="${this.serverChangeHandler}"
      >
        <slot name="custom-base-uri" slot="custom-base-uri"></slot>
      </api-server-picker>`;
    }
    /**
     * @returns The template for the server selector.
     */
    modelTemplate() {
        switch (this.renderedViewValue) {
            case 'summary': return this.summaryTemplate();
            case 'security': return this.securityTemplate();
            case 'documentation': return this.documentationTemplate();
            case 'schema': return this.schemaTemplate();
            case 'resource':
            case 'operation':
                return this.resourceTemplate();
            default: return '';
        }
    }
    /**
     * @returns The template for the API summary page.
     */
    summaryTemplate() {
        const { baseUri } = this;
        return html `
    <api-summary .baseUri="${baseUri}"></api-summary>`;
    }
    /**
     * @returns The template for the API security definition page.
     */
    securityTemplate() {
        const { domainId } = this;
        return html `<api-security-document
      .domainId="${domainId}"
      settingsOpened
    ></api-security-document>`;
    }
    /**
     * @returns The template for the RAML's documentation page.
     */
    documentationTemplate() {
        const { domainId } = this;
        return html `<api-documentation-document .domainId="${domainId}"></api-documentation-document>`;
    }
    /**
     * @returns The template for the API schema page.
     */
    schemaTemplate() {
        const { schemaMime, domainId } = this;
        return html `
    ${this.schemaMediaSelectorTemplate()}
    <api-schema-document
      .domainId="${domainId}"
      .mimeType="${schemaMime}"
      forceExamples
    ></api-schema-document>`;
    }
    /**
     * @returns The template for the API endpoint page.
     */
    resourceTemplate() {
        const { domainId, operationId, isAsync } = this;
        return html `<api-resource-document
      .domainId="${domainId}"
      .operationId="${operationId}"
      .redirectUri="${this.redirectUri}"
      .serverType="${this.serverType}"
      .serverValue="${this.serverValue}"
      .baseUri="${this.apiBaseUri}"
      ?tryItButton="${this.tryItButton}"
      ?tryItPanel="${this.tryItPanel}"
      ?httpUrlEditor="${this.httpUrlEditor}"
      httpNoServerSelector
      ?asyncApi="${isAsync}"
    ></api-resource-document>`;
    }
    /**
     * This is a part of schema rendering.
     * When the current API defines the media types then this shows the selector
     * to render examples with a specific mime.
     */
    schemaMediaSelectorTemplate() {
        const { documentMeta } = this;
        // only APIs have top level media types (?)
        const summary = this.apiSummaryValue;
        if (!summary || !documentMeta || !documentMeta.isApi) {
            return '';
        }
        const { accepts = [] } = summary;
        if (accepts.length < 2) {
            // if there's a single mime then we render the one we have. No selector needed.
            return '';
        }
        const mimeType = this.schemaMimeType || accepts[0];
        return html `
    <div class="media-type-selector">
      <span id="schema-mime">Schema content type</span>
      ${accepts.map((item) => html `
      <label class="label-large">
        <ui-radio
          class="schema-toggle"
          name="schemaMime"
          value="${item}"
          .checked="${item === mimeType}"
          @change="${this.mediaTypeSelectHandler}"
        ></ui-radio>
        ${item}
      </label>
      `)}
    </div>
    `;
    }
}
__decorate([
    property({ type: String })
], ApiDocumentation.prototype, "baseUri", void 0);
__decorate([
    property({ type: Boolean, reflect: true })
], ApiDocumentation.prototype, "tryItButton", void 0);
__decorate([
    property({ type: Boolean, reflect: true })
], ApiDocumentation.prototype, "tryItPanel", void 0);
__decorate([
    property({ type: String })
], ApiDocumentation.prototype, "redirectUri", void 0);
__decorate([
    property({ type: Boolean, reflect: true })
], ApiDocumentation.prototype, "httpUrlEditor", void 0);
__decorate([
    property({ type: Boolean, reflect: true })
], ApiDocumentation.prototype, "httpApplyAuthorization", void 0);
__decorate([
    property({ type: Array })
], ApiDocumentation.prototype, "httpCredentialsSource", void 0);
__decorate([
    property({ type: Boolean, reflect: true })
], ApiDocumentation.prototype, "noServerSelector", void 0);
__decorate([
    property({ type: Boolean, reflect: true })
], ApiDocumentation.prototype, "httpAllowCustom", void 0);
__decorate([
    property({ type: Boolean, reflect: true })
], ApiDocumentation.prototype, "allowCustomBaseUri", void 0);
__decorate([
    property({ type: String })
], ApiDocumentation.prototype, "serverValue", void 0);
__decorate([
    property({ type: String })
], ApiDocumentation.prototype, "serverType", void 0);
__decorate([
    property({ type: String })
], ApiDocumentation.prototype, "schemaMimeType", void 0);
__decorate([
    property({ type: String, reflect: true })
], ApiDocumentation.prototype, "domainType", null);
__decorate([
    property({ type: String, reflect: true })
], ApiDocumentation.prototype, "operationId", null);
__decorate([
    property({ type: Boolean, reflect: true })
], ApiDocumentation.prototype, "handleNavigationEvents", null);
//# sourceMappingURL=ApiDocumentation.element.js.map