import { downloadCsv } from "../services/csv";
import escapeHtml from "../services/dom";

/**
 * Returns the first matching element inside a container.
 *
 * @param {ParentNode} container
 *   The root element.
 * @param {string} selector
 *   The selector to match.
 *
 * @return {Element|null}
 *   The matching element.
 */
function select(container, selector) {
  return container ? container.querySelector(selector) : null;
}

/**
 * Returns all matching elements inside a container.
 *
 * @param {ParentNode} container
 *   The root element.
 * @param {string} selector
 *   The selector to match.
 *
 * @return {Array<Element>}
 *   The matching elements.
 */
function selectAll(container, selector) {
  return container ? Array.from(container.querySelectorAll(selector)) : [];
}

/**
 * Clones a DOM template fragment rendered by Twig.
 *
 * @param {HTMLElement} overlay
 *   The modal root element.
 * @param {string} name
 *   The template identifier.
 *
 * @return {Element|null}
 *   The cloned element.
 */
function cloneTemplate(overlay, name) {
  const template = select(overlay, `[data-no-explorer-template="${name}"]`);

  if (!template || !template.content || !template.content.firstElementChild) {
    return null;
  }

  return template.content.firstElementChild.cloneNode(true);
}

/**
 * Creates a technical explorer modal controller from DOM-rendered Twig markup.
 *
 * @param {HTMLElement} container
 *   The dashboard container.
 *
 * @return {Object|null}
 *   The modal controller.
 */
export default function createExplorerModal(container) {
  const overlay = select(container, "[data-no-explorer-modal-root]");

  if (!overlay) {
    return null;
  }

  const title = select(overlay, "[data-no-explorer-modal-title]");
  const subtitle = select(overlay, "[data-no-explorer-modal-subtitle]");
  const summary = select(overlay, "[data-no-explorer-modal-summary]");
  const tableTitle = select(overlay, "[data-no-explorer-modal-table-title]");
  const datasetBadge = select(
    overlay,
    "[data-no-explorer-modal-dataset-badge]",
  );
  const tableHead = select(overlay, "[data-no-explorer-modal-table-head]");
  const tableBody = select(overlay, "[data-no-explorer-modal-table-body]");
  const empty = select(overlay, "[data-no-explorer-modal-empty]");
  const detailTitle = select(overlay, "[data-no-explorer-modal-detail-title]");
  const detailDescription = select(
    overlay,
    "[data-no-explorer-modal-detail-description]",
  );
  const detailMeta = select(overlay, "[data-no-explorer-modal-detail-meta]");
  const payload = select(overlay, "[data-no-explorer-modal-payload]");
  const searchInput = select(overlay, "[data-no-explorer-modal-search]");
  const pageLabel = select(overlay, "[data-no-explorer-page-label]");
  const prevButton = select(overlay, '[data-no-explorer-page="prev"]');
  const nextButton = select(overlay, '[data-no-explorer-page="next"]');

  if (
    !title ||
    !subtitle ||
    !summary ||
    !tableTitle ||
    !datasetBadge ||
    !tableHead ||
    !tableBody ||
    !empty ||
    !detailTitle ||
    !detailDescription ||
    !detailMeta ||
    !payload ||
    !searchInput ||
    !pageLabel ||
    !prevButton ||
    !nextButton
  ) {
    return null;
  }

  const labels = {
    page: overlay.dataset.labelPage || Drupal.t("Page"),
    of: overlay.dataset.labelOf || Drupal.t("of"),
    noMetadata:
      overlay.dataset.labelNoMetadata ||
      Drupal.t("No metadata available for this record."),
    datasetDefault:
      overlay.dataset.labelDatasetDefault || Drupal.t("Technical Insight"),
    datasetSlowEndpoints:
      overlay.dataset.labelDatasetSlowEndpoints || Drupal.t("Slow Endpoints"),
    datasetAjaxEndpoints:
      overlay.dataset.labelDatasetAjaxEndpoints || Drupal.t("AJAX Endpoints"),
    datasetExternalRequests:
      overlay.dataset.labelDatasetExternalRequests ||
      Drupal.t("External Requests"),
    path: overlay.dataset.labelPath || Drupal.t("Path"),
    route: overlay.dataset.labelRoute || Drupal.t("Route"),
    liveTraceDetails:
      overlay.dataset.labelLiveTraceDetails || Drupal.t("Live trace details"),
    details: overlay.dataset.labelDetails || Drupal.t("Details"),
    capturedRows:
      overlay.dataset.labelCapturedRows || Drupal.t("Captured rows"),
  };

  const state = {
    datasetDetail: null,
    rows: [],
    filteredRows: [],
    currentPage: 1,
    pageSize: 5,
    activeRowId: null,
  };

  const close = () => {
    overlay.hidden = true;
    document.body.classList.remove("no-explorer-modal-open");
  };

  const resolveDatasetLabel = (type) => {
    switch (type) {
      case "slow_endpoints":
        return labels.datasetSlowEndpoints;
      case "ajax_endpoints":
        return labels.datasetAjaxEndpoints;
      case "external_requests":
        return labels.datasetExternalRequests;
      default:
        return labels.datasetDefault;
    }
  };

  const resolveSummaryIcon = (label) => {
    const normalized = String(label || "").toLowerCase();

    if (normalized.includes("range")) {
      return "◷";
    }
    if (normalized.includes("call")) {
      return "◎";
    }
    if (normalized.includes("duration")) {
      return "↯";
    }
    if (normalized.includes("error")) {
      return "⚠";
    }
    if (normalized.includes("method")) {
      return "⇄";
    }

    return "•";
  };

  const extractIdentityData = (summaryData) => {
    const identity = {
      path: "—",
      route: "—",
    };

    Object.entries(summaryData || {}).forEach(([label, value]) => {
      const normalized = String(label || "")
        .trim()
        .toLowerCase();
      const stringValue = String(value ?? "");

      if (normalized === "path / route") {
        const parts = stringValue.split("\n");
        identity.path = parts[0] || "—";
        identity.route = parts[1] || "—";
      } else if (normalized === "path") {
        identity.path = stringValue || "—";
      } else if (normalized === "route") {
        identity.route = stringValue || "—";
      }
    });

    return identity;
  };

  const buildMetaRows = (rowDetail) => {
    const summaryEntries = Object.entries(rowDetail?.summary || {});
    const identity = extractIdentityData(rowDetail?.summary || {});
    const excludedLabels = ["path / route", "path", "route"];
    const preferredOrder = [
      Drupal.t("trace id"),
      Drupal.t("request id"),
      Drupal.t("method"),
      Drupal.t("status code"),
      Drupal.t("duration"),
      Drupal.t("started"),
      Drupal.t("ended"),
      Drupal.t("ajax request"),
      Drupal.t("user id"),
      Drupal.t("client ip"),
      Drupal.t("target"),
      Drupal.t("parent request"),
    ];

    const metaEntries = summaryEntries
      .filter(
        ([label]) =>
          !excludedLabels.includes(
            String(label || "")
              .trim()
              .toLowerCase(),
          ),
      )
      .map(([label, value]) => ({
        label,
        value,
        normalized: String(label || "")
          .trim()
          .toLowerCase(),
      }));

    metaEntries.sort((left, right) => {
      const leftIndex = preferredOrder.indexOf(left.normalized);
      const rightIndex = preferredOrder.indexOf(right.normalized);

      if (leftIndex === -1 && rightIndex === -1) {
        return left.label.localeCompare(right.label);
      }
      if (leftIndex === -1) {
        return 1;
      }
      if (rightIndex === -1) {
        return -1;
      }

      return leftIndex - rightIndex;
    });

    const orderedRows = [
      {
        label: labels.path,
        value: identity.path,
        isPrimary: true,
        isRoute: false,
      },
      {
        label: labels.route,
        value: identity.route,
        isPrimary: true,
        isRoute: true,
      },
      ...metaEntries.map((entry) => ({
        label: entry.label,
        value: entry.value,
        isPrimary: false,
        isRoute: false,
      })),
    ];

    detailMeta.innerHTML = "";

    if (!orderedRows.length) {
      const emptyInline = cloneTemplate(overlay, "empty-inline");

      if (emptyInline) {
        emptyInline.textContent = labels.noMetadata;
        detailMeta.appendChild(emptyInline);
      }

      return;
    }

    orderedRows.forEach((entry) => {
      const row = cloneTemplate(overlay, "meta-row");

      if (!row) {
        return;
      }

      if (entry.isPrimary) {
        row.classList.add("is-primary");
      }

      const labelNode = select(row, '[data-role="label"]');
      const valueNode = select(row, '[data-role="value"]');

      if (labelNode) {
        labelNode.textContent = entry.label;
      }

      if (valueNode) {
        valueNode.textContent = entry.value;

        if (entry.isRoute) {
          valueNode.classList.add("is-route");
        }
      }

      detailMeta.appendChild(row);
    });
  };

  const renderDetail = (rowDetail, datasetDetail) => {
    detailTitle.textContent =
      datasetDetail?.detail_title || labels.liveTraceDetails;
    detailDescription.textContent = datasetDetail?.detail_description || "";
    buildMetaRows(rowDetail);
    payload.value = rowDetail?.payload_pretty || "";
  };

  const buildRowSearchText = (row) => {
    const cells = Array.isArray(row.cells) ? row.cells.join(" ") : "";
    const summaryValues = row.detail?.summary
      ? Object.values(row.detail.summary).join(" ")
      : "";
    return `${cells} ${summaryValues}`.toLowerCase();
  };

  const getStatusBadgeClass = (statusValue) => {
    const code = Number(statusValue);

    if (code >= 500) {
      return "is-error";
    }
    if (code >= 400) {
      return "is-warning";
    }
    if (code >= 300) {
      return "is-info";
    }
    if (code >= 200) {
      return "is-success";
    }

    return "";
  };

  const buildStatusBadge = (value) => {
    const badge = cloneTemplate(overlay, "status-badge");

    if (!badge) {
      return document.createTextNode(String(value ?? ""));
    }

    const stateClass = getStatusBadgeClass(value);
    if (stateClass) {
      badge.classList.add(stateClass);
    }

    badge.textContent = String(value ?? "");
    return badge;
  };

  const buildPathRouteCell = (cellValue) => {
    const wrapper = document.createElement("div");
    wrapper.className = "no-explorer-modal__path-route";

    const parts = String(cellValue || "").split("\n");
    const path = parts[0] || "—";
    const route = parts[1] || "—";

    const pathValue = document.createElement("div");
    pathValue.className = "no-explorer-modal__path-value";
    pathValue.textContent = path;

    const divider = document.createElement("div");
    divider.className = "no-explorer-modal__path-divider";

    const routeValue = document.createElement("div");
    routeValue.className = "no-explorer-modal__route-value";
    routeValue.textContent = route;

    wrapper.appendChild(pathValue);
    wrapper.appendChild(divider);
    wrapper.appendChild(routeValue);

    return wrapper;
  };

  const buildCellContent = (cell, columnName) => {
    const normalizedColumn = String(columnName || "").toLowerCase();

    if (normalizedColumn === "status") {
      return buildStatusBadge(cell);
    }

    if (normalizedColumn === "path / route") {
      return buildPathRouteCell(cell);
    }

    return document.createTextNode(String(cell ?? ""));
  };

  const renderTablePage = () => {
    const start = (state.currentPage - 1) * state.pageSize;
    const pageRows = state.filteredRows.slice(start, start + state.pageSize);
    const totalPages = Math.max(
      1,
      Math.ceil(state.filteredRows.length / state.pageSize),
    );

    empty.hidden = state.filteredRows.length > 0;
    pageLabel.textContent = `${labels.page} ${state.currentPage} ${labels.of} ${totalPages}`;

    prevButton.disabled = state.currentPage <= 1;
    nextButton.disabled = state.currentPage >= totalPages;

    tableBody.innerHTML = "";

    pageRows.forEach((row) => {
      const rowNode = cloneTemplate(overlay, "table-row");

      if (!rowNode) {
        return;
      }

      rowNode.setAttribute("data-no-explorer-modal-row", row.id || "");
      rowNode.setAttribute(
        "aria-selected",
        state.activeRowId === row.id ? "true" : "false",
      );

      if (state.activeRowId === row.id) {
        rowNode.classList.add("is-active");
      }

      (row.cells || []).forEach((cell, cellIndex) => {
        const cellNode = cloneTemplate(overlay, "table-cell");
        const columnName =
          state.datasetDetail?.table_columns?.[cellIndex] || "";

        if (!cellNode) {
          return;
        }

        cellNode.appendChild(buildCellContent(cell, columnName));
        rowNode.appendChild(cellNode);
      });

      rowNode.addEventListener("click", () => {
        state.activeRowId = row.id;
        renderTablePage();
        renderDetail(row.detail || {}, state.datasetDetail);
      });

      tableBody.appendChild(rowNode);
    });

    if (!pageRows.length) {
      renderDetail({}, state.datasetDetail);
      return;
    }

    const activeOnPage =
      pageRows.find((row) => row.id === state.activeRowId) || pageRows[0];
    state.activeRowId = activeOnPage.id;
    renderDetail(activeOnPage.detail || {}, state.datasetDetail);
  };

  const applyFilter = () => {
    const query = String(searchInput.value || "")
      .trim()
      .toLowerCase();

    state.filteredRows = !query
      ? [...state.rows]
      : state.rows.filter((row) => buildRowSearchText(row).includes(query));

    state.currentPage = 1;
    renderTablePage();
  };

  const exportRows = (mode) => {
    const rows = mode === "filtered" ? state.filteredRows : state.rows;
    const columns = (state.datasetDetail?.table_columns || []).map(
      (label, index) => ({
        key: index,
        label,
      }),
    );
    const exportRowsData = rows.map((row) => {
      const item = {};
      (row.cells || []).forEach((cell, index) => {
        item[index] = cell;
      });
      return item;
    });

    downloadCsv(
      `${state.datasetDetail?.type || "technical-insights"}-${mode}.csv`,
      columns,
      exportRowsData,
    );
  };

  const renderSummary = (datasetDetail) => {
    summary.innerHTML = "";

    Object.entries(datasetDetail.summary || {}).forEach(([label, value]) => {
      const card = cloneTemplate(overlay, "summary-card");

      if (!card) {
        return;
      }

      const iconNode = select(card, '[data-role="icon"]');
      const labelNode = select(card, '[data-role="label"]');
      const valueNode = select(card, '[data-role="value"]');

      if (iconNode) {
        iconNode.textContent = resolveSummaryIcon(label);
      }

      if (labelNode) {
        labelNode.textContent = label;
      }

      if (valueNode) {
        valueNode.textContent = String(value ?? "");
      }

      summary.appendChild(card);
    });
  };

  const open = (datasetDetail) => {
    state.datasetDetail = datasetDetail;
    state.rows = Array.isArray(datasetDetail.table_rows)
      ? datasetDetail.table_rows
      : [];
    state.filteredRows = [...state.rows];
    state.currentPage = 1;
    state.activeRowId = state.rows[0]?.id || null;

    title.textContent = datasetDetail.title || labels.details;
    subtitle.textContent = datasetDetail.subtitle || "";

    datasetBadge.textContent = resolveDatasetLabel(datasetDetail.type);
    datasetBadge.setAttribute(
      "data-dataset-type",
      datasetDetail.type || "default",
    );

    renderSummary(datasetDetail);

    tableTitle.textContent = datasetDetail.table_title || labels.capturedRows;
    tableHead.innerHTML = (datasetDetail.table_columns || [])
      .map((column) => `<th>${escapeHtml(column)}</th>`)
      .join("");

    searchInput.value = "";
    renderTablePage();

    overlay.hidden = false;
    document.body.classList.add("no-explorer-modal-open");
  };

  searchInput.addEventListener("input", applyFilter);

  prevButton.addEventListener("click", () => {
    if (state.currentPage > 1) {
      state.currentPage -= 1;
      renderTablePage();
    }
  });

  nextButton.addEventListener("click", () => {
    const totalPages = Math.max(
      1,
      Math.ceil(state.filteredRows.length / state.pageSize),
    );

    if (state.currentPage < totalPages) {
      state.currentPage += 1;
      renderTablePage();
    }
  });

  selectAll(overlay, "[data-no-explorer-modal-export]").forEach((button) => {
    button.addEventListener("click", () => {
      exportRows(button.getAttribute("data-no-explorer-modal-export"));
    });
  });

  selectAll(overlay, "[data-no-explorer-modal-close]").forEach((element) => {
    element.addEventListener("click", close);
  });

  document.addEventListener("keydown", (event) => {
    if (event.key === "Escape" && !overlay.hidden) {
      close();
    }
  });

  return { open, close };
}
