import { LitElement, html, css } from "lit";
import { customElement, property } from "lit/decorators.js";
import { unsafeHTML } from "lit/directives/unsafe-html.js";
import { sanitizeHTML } from "../utils/sanitize.js";
import { marked } from "marked";
import { prepareResponseDefinitions } from "../utils/response-utils.js";
import { exampleLabel } from "~/utils/schema-utils";
import FontStyles from "~/styles/font-styles";
import FlexStyles from "~/styles/flex-styles";
import ParamListStyles from "~/styles/param-list-styles";
import InputStyles from "~/styles/input-styles";
import TabStyles from "~/styles/tab-styles";
import BorderStyles from "~/styles/border-styles";
import CustomStyles from "~/styles/custom-styles";
import "~/components/json-tree";
import "~/components/schema-tree";
import "~/components/schema-table";

@customElement("api-response")
export default class ApiResponse extends LitElement {
  @property({ type: Object }) config: any;
  @property({ type: String }) callback = "";
  @property({ type: String }) webhook = "";
  @property({ type: Object }) responses: any;
  @property({ type: Object }) parser: any;
  @property({ type: String, attribute: "schema-style" }) schemaStyle = "";
  @property({ type: String, attribute: "render-style" }) renderStyle = "";
  @property({ type: String, attribute: "selected-status" }) selectedStatus = "";
  @property({ type: String, attribute: "selected-mime-type" }) selectedMimeType = "";
  @property({ type: String, attribute: "active-schema-tab" }) activeSchemaTab = "schema";
  @property({ type: Number, attribute: "schema-expand-level" }) schemaExpandLevel = 99999;
  @property({ type: String, attribute: "schema-description-expanded" }) schemaDescriptionExpanded =
    "false";
  @property({ type: String, attribute: "allow-schema-description-expand-toggle" })
  allowSchemaDescriptionExpandToggle = "false";
  @property({ type: String, attribute: "schema-hide-read-only" }) schemaHideReadOnly = "false";
  @property({ type: String, attribute: "schema-hide-write-only" }) schemaHideWriteOnly = "true";

  defaultStatus = "";
  defaultMimeType = "";
  headersForEachRespStatus: any = {};
  mimeResponsesForEachStatus: any = {};

  constructor() {
    super();
    this.selectedStatus = "";
    this.defaultStatus = "";
    this.defaultMimeType = "";
    this.headersForEachRespStatus = {};
    this.mimeResponsesForEachStatus = {};
    this.activeSchemaTab = "schema";
  }

  static get styles() {
    return [
      FontStyles,
      FlexStyles,
      TabStyles,
      ParamListStyles,
      InputStyles,
      BorderStyles,
      css`
        .resp-headers-list {
          margin-bottom: 16px;
        }
        :where(button, input[type='checkbox'], [tabindex='0']):focus-visible {
          box-shadow: var(--focus-shadow);
        }
        :where(input[type='text'], input[type='password'], select, textarea):focus-visible {
          border-color: var(--primary);
        }
        .resp-head {
          vertical-align: middle;
          padding: 16px 0 8px;

          &.divider {
            border-top: 1px solid var(--border);
            margin-top: 10px;
          }
        }
        .resp-status {
          font-weight: bold;
          font-size: calc(var(--font-size-small) + 1px);
        }
        .resp-descr {
          font-size: var(--font-size-description);
          color: var(--muted-foreground);
          text-align: left;
        }
        .top-gap {
          margin-top: 16px;
        }
        .example-panel {
          font-size: var(--font-size-small);
          margin: 0;
        }
        /* Response codes: a segmented control (one choice), the selected code tinted with the primary color */
        .status-group {
          display: inline-flex;
          flex-wrap: wrap;
          margin-top: 8px;
        }
        .status-btn {
          margin: 0 0 0 -1px;
          border-color: var(--input);
          border-radius: 0;
          background: color-mix(in srgb, var(--foreground) 5%, transparent);
          color: var(--muted-foreground);

          &:first-child {
            margin-left: 0;
            border-start-start-radius: var(--radius);
            border-end-start-radius: var(--radius);
          }
          &:last-child {
            border-start-end-radius: var(--radius);
            border-end-end-radius: var(--radius);
          }
          &:hover {
            background: var(--muted);
            color: var(--foreground);
          }
          &[aria-pressed='true'] {
            position: relative;
            z-index: 1;
            border-color: var(--primary);
            background: color-mix(in srgb, var(--primary) 14%, transparent);
            color: var(--primary);
          }
          &:focus-visible {
            z-index: 2;
          }
        }
      `,
      CustomStyles,
    ];
  }

  willUpdate(changedProperties) {
    super.willUpdate?.(changedProperties);
    // Sync configuration options passed down from parent <rapi-doc>
    if (this.config && changedProperties.has("config")) {
      if (this.config.renderStyle) this.renderStyle = this.config.renderStyle;
      if (this.config.schemaStyle) this.schemaStyle = this.config.schemaStyle;
      if (this.config.schemaExpandLevel !== undefined)
        this.schemaExpandLevel = this.config.schemaExpandLevel;
      if (this.config.schemaDescriptionExpanded !== undefined)
        this.schemaDescriptionExpanded = this.config.schemaDescriptionExpanded;
      if (this.config.allowSchemaDescriptionExpandToggle !== undefined)
        this.allowSchemaDescriptionExpandToggle = this.config.allowSchemaDescriptionExpandToggle;
      // In responses, readOnly fields are shown by default ('false') while writeOnly fields are hidden ('true').
      // For webhooks, this behavior is inverted unless configured to 'never'.
      if (this.config.schemaHideReadOnly !== undefined)
        this.schemaHideReadOnly =
          this.config.schemaHideReadOnly === "never"
            ? "false"
            : this.webhook === "true"
              ? "true"
              : "false";
      if (this.config.schemaHideWriteOnly !== undefined)
        this.schemaHideWriteOnly =
          this.config.schemaHideWriteOnly === "never"
            ? "false"
            : this.webhook === "true"
              ? "false"
              : "true";
      if (this.config.defaultSchemaTab) this.activeSchemaTab = this.config.defaultSchemaTab;
    }
    // Parse response definitions, headers, examples, and AST schemas when responses change
    if (
      changedProperties.has("responses") ||
      changedProperties.has("callback") ||
      changedProperties.has("webhook")
    ) {
      if (!this.responses || typeof this.responses !== "object") {
        return;
      }
      const defs = prepareResponseDefinitions(this.responses, {
        callback: this.callback,
        webhook: this.webhook,
      });
      this.headersForEachRespStatus = defs.headersForEachRespStatus;
      this.mimeResponsesForEachStatus = defs.mimeResponsesForEachStatus;
      this.defaultStatus = defs.defaultStatus;
      this.defaultMimeType = defs.defaultMimeType;
      // Default to primary status code (e.g. '200') if none is currently selected or if the old status doesn't exist on this response
      if (!this.selectedStatus || !this.responses?.[this.selectedStatus]) {
        this.selectedStatus = defs.defaultStatus;
      }
      if (!this.selectedMimeType) {
        this.selectedMimeType = defs.defaultMimeType;
      }
    }
  }

  render() {
    return html`<div class="col regular-font response-panel ${this.renderStyle}-mode">
      ${
        this.callback === "true"
          ? html`<h6 class="rapidoc-heading label-heading tiny-title" part="section-response-title heading heading-6">
              Callback response
            </h6>`
          : html`<h4 class="rapidoc-heading label-heading req-res-title" part="section-response-title heading heading-4">Response</h4>`
      }
      <div>${this.responseTemplate()}</div>
    </div> `;
  }

  // Called by rapi-doc after navigating in focused mode.
  // In focused mode, the single <api-response> element instance is reused across operations.
  // We reset selections back to the default status code and MIME type of the active endpoint,
  // preventing user selections from a prior endpoint (e.g., status 400) from carrying over or hiding panels.
  resetSelection() {
    this.selectedStatus =
      this.defaultStatus ||
      (this.responses ? Object.keys(this.responses).find((k) => k !== "$$ref") || "" : "");
    if (this.selectedStatus && this.mimeResponsesForEachStatus?.[this.selectedStatus]) {
      this.selectedMimeType =
        Object.keys(this.mimeResponsesForEachStatus[this.selectedStatus])[0] || "";
    } else {
      this.selectedMimeType = this.defaultMimeType || "";
    }
  }

  responseTemplate() {
    if (!this.responses) {
      return "";
    }
    // Fallback guard: Ensure selectedStatus and selectedMimeType always point to valid options
    // for this endpoint, even if resetSelection was called or responses have not re-rendered yet.
    if (!this.selectedStatus || !this.responses[this.selectedStatus]) {
      this.selectedStatus =
        this.defaultStatus ||
        Object.keys(this.responses).find((k) => k !== "$$ref") ||
        Object.keys(this.responses)[0] ||
        "";
    }
    if (
      !this.selectedMimeType &&
      this.selectedStatus &&
      this.mimeResponsesForEachStatus?.[this.selectedStatus]
    ) {
      this.selectedMimeType =
        Object.keys(this.mimeResponsesForEachStatus[this.selectedStatus])[0] ||
        this.defaultMimeType ||
        "";
    }
    return html`
      ${
        Object.keys(this.responses).length > 1
          ? html`<div class="status-group" role="group" aria-label="Response status">
              ${Object.keys(this.responses).map(
                (respStatus) =>
                  html` ${
                    respStatus === "$$ref" // Ignore '$$ref' if present from legacy reference parsing
                      ? ""
                      : html`<button
                          @click="${() => {
                            this.selectedStatus = respStatus;
                            if (
                              this.responses[respStatus].content &&
                              Object.keys(this.responses[respStatus].content)[0]
                            ) {
                              this.selectedMimeType = Object.keys(
                                this.responses[respStatus].content,
                              )[0];
                            } else {
                              this.selectedMimeType = undefined;
                            }
                          }}"
                          class="btn btn-sm status-btn"
                          aria-pressed="${this.selectedStatus === respStatus ? "true" : "false"}"
                          part="btn ${
                            this.selectedStatus === respStatus
                              ? "btn-response-status btn-selected-response-status"
                              : " btn-response-status"
                          }"
                        >
                          ${respStatus}
                        </button>`
                  }`,
              )}
            </div>`
          : html`<span>${Object.keys(this.responses)[0]}</span>`
      }
      ${Object.keys(this.responses).map(
        (status) =>
          html`<div style="display: ${status === this.selectedStatus ? "block" : "none"}">
            <div class="top-gap">
              <span class="resp-descr m-markdown">${unsafeHTML(sanitizeHTML(marked(this.responses[status]?.description || "")))}</span>
              ${
                this.headersForEachRespStatus[status] &&
                this.headersForEachRespStatus[status]?.length > 0
                  ? html`${this.responseHeaderListTemplate(this.headersForEachRespStatus[status])}`
                  : ""
              }
            </div>
            ${
              !this.mimeResponsesForEachStatus[status] ||
              Object.keys(this.mimeResponsesForEachStatus[status]).length === 0
                ? ""
                : html`<div part="tab-panel" class="tab-panel col">
                    <div
                      part="tab-btn-row"
                      class="tab-buttons row"
                      @click="${(e) => {
                        if (e.target.tagName.toLowerCase() === "button") {
                          this.activeSchemaTab = e.target.dataset.tab;
                        }
                      }}"
                    >
                      <button part="tab-btn" class="tab-btn ${this.activeSchemaTab === "example" ? "active" : ""}" data-tab="example">
                        EXAMPLE
                      </button>
                      <button part="tab-btn" class="tab-btn ${this.activeSchemaTab !== "example" ? "active" : ""}" data-tab="schema">
                        SCHEMA
                      </button>
                      ${
                        Object.keys(this.mimeResponsesForEachStatus[status]).length === 1
                          ? html`<span class="tab-bar-end">${Object.keys(this.mimeResponsesForEachStatus[status])[0]}</span>`
                          : html`${this.mimeTypeDropdownTemplate(Object.keys(this.mimeResponsesForEachStatus[status]))}`
                      }
                    </div>
                    ${
                      this.activeSchemaTab === "example"
                        ? html`<div part="tab-content" class="tab-content col">
                            ${this.mimeExampleTemplate(this.mimeResponsesForEachStatus[status][this.selectedMimeType])}
                          </div>`
                        : html`<div part="tab-content" class="tab-content col">
                            ${this.mimeSchemaTemplate(this.mimeResponsesForEachStatus[status][this.selectedMimeType])}
                          </div>`
                    }
                  </div> `
            }
          </div>`,
      )}
    `;
  }

  responseHeaderListTemplate(respHeaders) {
    return html`<h5 class="rapidoc-heading label-heading resp-headers" part="heading heading-5">Response headers</h5>
      <div class="param-list-container resp-headers-list">
        <ul class="param-list" part="param-list" role="list">
          ${respHeaders.map(
            (v) =>
              html`<li class="param" part="param">
                <div class="param-label" part="param-label">
                  <div class="param-name" part="param-name">${v.name || ""}</div>
                  ${v.schema?.type ? html`<div class="param-type" part="param-type">${v.schema.type}</div>` : ""}
                </div>
                ${
                  v.schema?.example
                    ? html`<dl class="param-constraint" part="param-constraint">
                        <dt class="param-constraint-label" part="param-constraint-label">Example:</dt>
                        <dd>${v.schema.example}</dd>
                      </dl>`
                    : ""
                }
                <div class="param-details" part="param-details">
                  ${v.description ? html`<div class="m-markdown-small">${unsafeHTML(sanitizeHTML(marked(v.description)))}</div>` : ""}
                </div>
              </li>`,
          )}
        </ul>
      </div>`;
  }

  mimeTypeDropdownTemplate(mimeTypes) {
    return html`<select
      aria-label="mime types"
      class="select-sm tab-bar-end"
      @change="${(e) => {
        this.selectedMimeType = e.target.value;
      }}"
    >
      ${mimeTypes.map(
        (mimeType) =>
          html`<option value="${mimeType}" ?selected="${mimeType === this.selectedMimeType}">${mimeType}</option>`,
      )}
    </select>`;
  }

  onSelectExample(e) {
    const exampleContainerEl = e.target.closest(".example-panel");
    const exampleEls = [...exampleContainerEl.querySelectorAll(".example")];

    exampleEls.forEach((v) => {
      v.style.display = v.dataset.example === e.target.value ? "block" : "none";
    });
  }

  mimeExampleTemplate(mimeRespDetails) {
    if (!mimeRespDetails || !mimeRespDetails.examples || mimeRespDetails.examples.length === 0) {
      return html` <pre style="color:var(--destructive)" class="example-panel"> No example provided </pre> `;
    }
    return html`
      ${
        mimeRespDetails.examples.length === 1
          ? html` ${
              mimeRespDetails.examples[0].exampleFormat === "json"
                ? html` ${
                    mimeRespDetails.examples[0].exampleSummary &&
                    mimeRespDetails.examples[0].exampleSummary.length > 80
                      ? html`<div style="padding: 4px 0">${mimeRespDetails.examples[0].exampleSummary}</div>`
                      : ""
                  }
                    ${
                      mimeRespDetails.examples[0].exampleDescription
                        ? html`<div class="m-markdown-small" style="padding: 4px 0">
                            ${unsafeHTML(sanitizeHTML(marked(mimeRespDetails.examples[0].exampleDescription || "")))}
                          </div>`
                        : ""
                    }
                    <json-tree
                      render-style="${this.renderStyle}"
                      .data="${mimeRespDetails.examples[0].exampleValue}"
                      class="example-panel"
                      exportparts="btn:btn, btn-outline:btn-outline, btn-copy:btn-copy"
                    ></json-tree>`
                : html`
                    ${
                      mimeRespDetails.examples[0].exampleSummary &&
                      mimeRespDetails.examples[0].exampleSummary.length > 80
                        ? html`<div style="padding: 4px 0">${mimeRespDetails.examples[0].exampleSummary}</div>`
                        : ""
                    }
                    ${
                      mimeRespDetails.examples[0].exampleDescription
                        ? html`<div class="m-markdown-small" style="padding: 4px 0">
                            ${unsafeHTML(sanitizeHTML(marked(mimeRespDetails.examples[0].exampleDescription || "")))}
                          </div>`
                        : ""
                    }
                    <pre class="example-panel">${mimeRespDetails.examples[0].exampleValue}</pre>
                  `
            }`
          : html`
              <div class="example-panel">
                <select
                  aria-label="response examples"
                  class="select-sm example-select"
                  title="${exampleLabel(mimeRespDetails.examples.find((v) => v.exampleId === mimeRespDetails.selectedExample))}"
                  @change="${(e) => this.onSelectExample(e)}"
                >
                  ${mimeRespDetails.examples.map(
                    (v) =>
                      html`<option value="${v.exampleId}" title="${exampleLabel(v)}" ?selected=${v.exampleId === mimeRespDetails.selectedExample}>
                        ${exampleLabel(v)}
                      </option>`,
                  )}
                </select>
                ${mimeRespDetails.examples.map(
                  (v) => html`
                    <div
                      class="example"
                      data-example="${v.exampleId}"
                      style="display: ${v.exampleId === mimeRespDetails.selectedExample ? "block" : "none"}"
                    >
                      ${
                        v.exampleDescription
                          ? html`<div class="m-markdown-small" style="padding: 4px 0">
                              ${unsafeHTML(sanitizeHTML(marked(v.exampleDescription || "")))}
                            </div>`
                          : ""
                      }
                      ${
                        v.exampleFormat === "json"
                          ? html`<json-tree
                              render-style="${this.renderStyle}"
                              .data="${v.exampleValue}"
                              exportparts="btn:btn, btn-outline:btn-outline, btn-copy:btn-copy"
                            ></json-tree>`
                          : html`<pre>${v.exampleValue}</pre>`
                      }
                    </div>
                  `,
                )}
              </div>
            `
      }
    `;
  }

  mimeSchemaTemplate(mimeRespDetails) {
    if (!mimeRespDetails) {
      return html` <pre style="color:var(--destructive)" class="example-panel"> Schema not found</pre> `;
    }
    return html` ${
      this.schemaStyle === "table"
        ? html`
            <schema-table
              .data="${mimeRespDetails.schemaTree}"
              schema-expand-level="${this.schemaExpandLevel}"
              schema-description-expanded="${this.schemaDescriptionExpanded}"
              allow-schema-description-expand-toggle="${this.allowSchemaDescriptionExpandToggle}"
              schema-hide-read-only="${this.schemaHideReadOnly}"
              schema-hide-write-only="${this.schemaHideWriteOnly}"
              exportparts="schema-description:schema-description, schema-multiline-toggle:schema-multiline-toggle"
            >
            </schema-table>
          `
        : html`<schema-tree
            .data="${mimeRespDetails.schemaTree}"
            schema-expand-level="${this.schemaExpandLevel}"
            schema-description-expanded="${this.schemaDescriptionExpanded}"
            allow-schema-description-expand-toggle="${this.allowSchemaDescriptionExpandToggle}"
            schema-hide-read-only="${this.schemaHideReadOnly}"
            schema-hide-write-only="${this.schemaHideWriteOnly}"
            exportparts="schema-description:schema-description, schema-multiline-toggle:schema-multiline-toggle"
          >
          </schema-tree>`
    }`;
  }
}
