import { createChartFallback, initResponsiveEchart } from "../services/echarts";

/**
 * Returns a cloned template fragment from the current section.
 *
 * @param {HTMLElement} section
 *   The database section.
 * @param {string} name
 *   The template name.
 *
 * @return {DocumentFragment|null}
 *   The cloned fragment.
 */
function cloneTemplate(section, name) {
  const template = section.querySelector(`[data-no-db-template="${name}"]`);
  return template?.content ? template.content.cloneNode(true) : null;
}

/**
 * Builds the pressure trend chart option.
 *
 * @param {HTMLElement} host
 *   The section host element.
 * @param {Object} settings
 *   The chart settings from drupalSettings.
 *
 * @return {Object}
 *   The ECharts option object.
 */
function buildPressureTrendOption(host, settings) {
  const labels = Array.isArray(settings.labels) ? settings.labels : [];
  const series = Array.isArray(settings.series) ? settings.series : [];

  return {
    animation: true,
    tooltip: {
      trigger: "axis",
      confine: true,
    },
    legend: {
      top: 8,
      left: "center",
      data: series.map((item) => item.name ?? ""),
    },
    grid: {
      top: 56,
      right: 24,
      bottom: 40,
      left: 68,
      containLabel: true,
    },
    xAxis: {
      type: "category",
      boundaryGap: false,
      data: labels,
    },
    yAxis: [
      {
        type: "value",
        name: host.dataset.axisPrimaryLabel || "",
        nameLocation: "middle",
        nameRotate: 90,
        nameGap: 52,
        nameTextStyle: {
          align: "center",
          verticalAlign: "middle",
        },
      },
      {
        type: "value",
        name: host.dataset.axisSecondaryLabel || "",
        nameGap: 18,
      },
    ],
    series: series.map((item, index) => ({
      name: item.name ?? "",
      type: item.type ?? "line",
      smooth: item.type !== "bar",
      symbol: item.type === "bar" ? "none" : "circle",
      symbolSize: item.type === "bar" ? 0 : 6,
      yAxisIndex: index === 2 ? 1 : 0,
      ...(item.type !== "bar"
        ? {
            lineStyle: { width: 2 },
          }
        : {}),
      ...(item.type === "line" && index === 0
        ? {
            areaStyle: { opacity: 0.08 },
          }
        : {}),
      ...(item.type === "bar"
        ? {
            barMaxWidth: 22,
          }
        : {}),
      data: Array.isArray(item.data)
        ? item.data.map((value) => Number(value ?? 0))
        : [],
    })),
  };
}

/**
 * Builds the request comparison chart option.
 *
 * @param {HTMLElement} host
 *   The section host element.
 * @param {Object} settings
 *   The chart settings from drupalSettings.
 *
 * @return {Object}
 *   The ECharts option object.
 */
function buildRequestComparisonOption(host, settings) {
  const points = Array.isArray(settings.points) ? settings.points : [];

  return {
    animation: true,
    tooltip: {
      trigger: "item",
      confine: true,
      formatter(params) {
        const data = params.data || [];
        return [
          `<strong>${data[2] || ""}</strong>`,
          `${host.dataset.tooltipRequestDurationLabel}: ${data[0]} ms`,
          `${host.dataset.tooltipDbTimeLabel}: ${data[1]} ms`,
          `${host.dataset.tooltipRequestCountLabel}: ${data[3]}`,
          `${host.dataset.tooltipSlowRateLabel}: ${data[4]}%`,
        ].join("<br>");
      },
    },
    grid: {
      top: 24,
      right: 24,
      bottom: 52,
      left: 84,
      containLabel: true,
    },
    xAxis: {
      type: "value",
      name: host.dataset.axisXLabel || "",
      nameLocation: "middle",
      nameGap: 30,
    },
    yAxis: {
      type: "value",
      name: host.dataset.axisYLabel || "",
      nameLocation: "middle",
      nameRotate: 90,
      nameGap: 60,
      nameTextStyle: {
        align: "center",
        verticalAlign: "middle",
      },
    },
    series: [
      {
        type: "scatter",
        symbolSize(data) {
          const requests = Number(data[3] ?? 0);
          return Math.max(12, Math.min(46, 10 + requests * 1.2));
        },
        data: points.map((point) => [
          Number(point.request_duration_ms ?? 0),
          Number(point.db_time_ms ?? 0),
          point.label ?? "",
          Number(point.request_count ?? 0),
          Number(point.slow_rate ?? 0),
        ]),
      },
    ],
  };
}

/**
 * Populates the modal summary area using DOM templates.
 *
 * @param {HTMLElement} section
 *   The database section.
 * @param {HTMLElement} modal
 *   The modal element.
 * @param {Object|null} summary
 *   The summary payload.
 */
function populateModalSummary(section, modal, summary) {
  const summaryWrapper = modal.querySelector(".no-db-modal__summary");
  if (!summaryWrapper) {
    return;
  }

  summaryWrapper.innerHTML = "";

  if (!summary) {
    summaryWrapper.hidden = true;
    return;
  }

  const items = [
    {
      label: summary.primary_label || "",
      value: summary.primary_value || "",
    },
    {
      label: summary.secondary_label || "",
      value: summary.secondary_value || "",
    },
  ];

  items.forEach((item) => {
    const fragment = cloneTemplate(section, "modal-summary-card");
    if (!fragment) {
      return;
    }

    const label = fragment.querySelector('[data-db-modal-field="label"]');
    const value = fragment.querySelector('[data-db-modal-field="value"]');

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

    if (value) {
      value.textContent = item.value;
    }

    summaryWrapper.appendChild(fragment);
  });

  summaryWrapper.hidden = false;
}

/**
 * Populates the modal table area using DOM templates.
 *
 * @param {HTMLElement} section
 *   The database section.
 * @param {HTMLElement} modal
 *   The modal element.
 * @param {Object} payload
 *   The modal payload.
 */
function populateModalTable(section, modal, payload) {
  const headers = Array.isArray(payload.headers) ? payload.headers : [];
  const rows = Array.isArray(payload.rows) ? payload.rows : [];

  const tableWrap = modal.querySelector(".no-db-modal__table-wrap");
  const theadRow = modal.querySelector(".no-db-modal__table thead tr");
  const tbody = modal.querySelector(".no-db-modal__table tbody");
  const emptyStateHost = modal.querySelector(".no-db-modal__empty-state");

  if (!tableWrap || !theadRow || !tbody || !emptyStateHost) {
    return;
  }

  theadRow.innerHTML = "";
  tbody.innerHTML = "";
  emptyStateHost.innerHTML = "";

  headers.forEach((header) => {
    const th = document.createElement("th");
    th.textContent = header;
    theadRow.appendChild(th);
  });

  if (!rows.length) {
    const fragment = cloneTemplate(section, "modal-empty-state");
    if (fragment) {
      const emptyText = fragment.querySelector("[data-db-modal-empty-text]");
      if (emptyText) {
        emptyText.textContent = payload.empty_message || "";
      }
      emptyStateHost.appendChild(fragment);
    }

    emptyStateHost.hidden = false;
    tableWrap.hidden = true;
    return;
  }

  rows.forEach((row) => {
    const fragment = cloneTemplate(section, "modal-table-row");
    if (!fragment) {
      return;
    }

    const mapping = {
      time: row.time || "",
      duration: row.duration || "",
      route: row.route || "",
      connection: row.connection || "",
      hash: row.hash || "",
      caller: row.caller || "",
    };

    Object.entries(mapping).forEach(([key, value]) => {
      const cell = fragment.querySelector(`[data-db-modal-column="${key}"]`);
      if (!cell) {
        return;
      }

      if (key === "hash") {
        const code = document.createElement("code");
        code.textContent = value;
        cell.appendChild(code);
      } else {
        cell.textContent = value;
      }
    });

    tbody.appendChild(fragment);
  });

  emptyStateHost.hidden = true;
  tableWrap.hidden = false;
}

/**
 * Initializes the section modal.
 *
 * @param {HTMLElement} section
 *   The section element.
 */
function initDatabaseModal(section) {
  const modal = section.querySelector(".no-db-modal");
  if (!modal) {
    return;
  }

  const title = modal.querySelector(".no-db-modal__title");
  const closeButton = modal.querySelector(".no-db-modal__close");
  const backdrop = modal.querySelector(".no-db-modal__backdrop");

  const closeModal = () => {
    modal.hidden = true;
  };

  const openModal = (payload) => {
    if (title) {
      title.textContent = payload.title || section.dataset.modalTitle || "";
    }

    populateModalSummary(section, modal, payload.summary || null);
    populateModalTable(section, modal, payload);
    modal.hidden = false;
  };

  section.querySelectorAll(".no-db-modal-trigger").forEach((button) => {
    button.addEventListener("click", () => {
      const rawPayload = button.dataset.dbModalPayload || "";
      if (!rawPayload) {
        return;
      }

      try {
        openModal(JSON.parse(rawPayload));
      } catch (error) {
        closeModal();
      }
    });
  });

  closeButton?.addEventListener("click", closeModal);
  backdrop?.addEventListener("click", closeModal);

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

/**
 * Converts table rows to exportable CSV.
 *
 * @param {HTMLTableElement} table
 *   The source table.
 *
 * @return {string}
 *   The CSV string.
 */
function buildCsv(table) {
  const rows = [...table.querySelectorAll("tr")].filter((row) => !row.hidden);

  return rows
    .map((row) => {
      return [...row.querySelectorAll("th, td")]
        .map((cell) => {
          const text = (cell.textContent || "").trim().replace(/\s+/g, " ");
          return `"${text.replace(/"/g, '""')}"`;
        })
        .join(",");
    })
    .join("\n");
}

/**
 * Downloads a CSV file.
 *
 * @param {string} content
 *   The CSV content.
 * @param {string} filename
 *   The export file name.
 */
function downloadCsv(content, filename = "database-export.csv") {
  const blob = new Blob([content], { type: "text/csv;charset=utf-8;" });
  const url = URL.createObjectURL(blob);
  const link = document.createElement("a");

  link.href = url;
  link.download = filename;
  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);
  URL.revokeObjectURL(url);
}

/**
 * Compares sortable values.
 *
 * @param {string} left
 *   The left value.
 * @param {string} right
 *   The right value.
 *
 * @return {number}
 *   The comparison result.
 */
function compareSortableValues(left, right) {
  const leftNumber = Number(left);
  const rightNumber = Number(right);
  const bothNumeric = !Number.isNaN(leftNumber) && !Number.isNaN(rightNumber);

  if (bothNumeric) {
    return leftNumber - rightNumber;
  }

  const leftTimestamp = Date.parse(left);
  const rightTimestamp = Date.parse(right);
  const bothDates =
    !Number.isNaN(leftTimestamp) && !Number.isNaN(rightTimestamp);

  if (bothDates) {
    return leftTimestamp - rightTimestamp;
  }

  const collator = new Intl.Collator(undefined, {
    numeric: true,
    sensitivity: "base",
  });

  return collator.compare(left, right);
}

/**
 * Creates a reusable client-side table controller.
 *
 * @param {Object} options
 *   The table options.
 */
function initInteractiveTable(options) {
  const {
    table,
    searchInput,
    emptyState,
    prevButton,
    nextButton,
    status,
    exportButton,
    exportFilename,
    defaultSortIndex = 0,
    defaultSortDirection = "asc",
  } = options;

  if (!table) {
    return;
  }

  const tbody = table.querySelector("tbody");
  if (!tbody) {
    return;
  }

  const allRows = [...tbody.querySelectorAll("tr")];
  const pageSize = Number(table.dataset.pageSize || 10);

  let currentPage = 1;
  let sortIndex = defaultSortIndex;
  let sortDirection = defaultSortDirection;
  let searchTerm = "";

  const getFilteredRows = () => {
    let rows = [...allRows];

    if (searchTerm) {
      rows = rows.filter((row) => {
        const content = (row.textContent || "").toLowerCase();
        return content.includes(searchTerm);
      });
    }

    rows.sort((a, b) => {
      const aCell = a.children[sortIndex];
      const bCell = b.children[sortIndex];

      const aValue = (
        aCell?.dataset.sortValue ||
        aCell?.textContent ||
        ""
      ).trim();
      const bValue = (
        bCell?.dataset.sortValue ||
        bCell?.textContent ||
        ""
      ).trim();

      const result = compareSortableValues(aValue, bValue);
      return sortDirection === "asc" ? result : -result;
    });

    return rows;
  };

  const updateSortHeaders = () => {
    table.querySelectorAll("thead th.is-sortable").forEach((header) => {
      const headerIndex = Number(header.dataset.sortKey);

      if (headerIndex !== sortIndex) {
        header.setAttribute("aria-sort", "none");
        return;
      }

      header.setAttribute(
        "aria-sort",
        sortDirection === "asc" ? "ascending" : "descending",
      );
    });
  };

  const render = () => {
    const rows = getFilteredRows();
    const totalRows = rows.length;
    const totalPages = Math.max(1, Math.ceil(totalRows / pageSize));

    currentPage = Math.min(currentPage, totalPages);

    const start = (currentPage - 1) * pageSize;
    const end = start + pageSize;
    const pageRows = rows.slice(start, end);

    allRows.forEach((row) => {
      row.hidden = true;
    });

    pageRows.forEach((row) => {
      row.hidden = false;
    });

    if (emptyState) {
      emptyState.hidden = totalRows > 0;
    }

    if (prevButton) {
      prevButton.disabled = currentPage <= 1 || totalRows === 0;
    }

    if (nextButton) {
      nextButton.disabled = currentPage >= totalPages || totalRows === 0;
    }

    if (status) {
      status.textContent = totalRows
        ? Drupal.t("Page @current of @total", {
            "@current": currentPage,
            "@total": totalPages,
          })
        : Drupal.t("Page 0 of 0");
    }

    updateSortHeaders();
  };

  searchInput?.addEventListener("input", () => {
    searchTerm = (searchInput.value || "").trim().toLowerCase();
    currentPage = 1;
    render();
  });

  exportButton?.addEventListener("click", () => {
    downloadCsv(buildCsv(table), exportFilename);
  });

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

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

    if (currentPage < totalPages) {
      currentPage += 1;
      render();
    }
  });

  table.querySelectorAll("thead th.is-sortable").forEach((th) => {
    const handleSort = () => {
      const nextIndex = Number(th.dataset.sortKey);

      if (sortIndex === nextIndex) {
        sortDirection = sortDirection === "asc" ? "desc" : "asc";
      } else {
        sortIndex = nextIndex;
        sortDirection =
          nextIndex === defaultSortIndex ? defaultSortDirection : "asc";
      }

      currentPage = 1;
      render();
    };

    th.addEventListener("click", handleSort);
    th.addEventListener("keydown", (event) => {
      if (event.key === "Enter" || event.key === " ") {
        event.preventDefault();
        handleSort();
      }
    });
  });

  render();
}

/**
 * Initializes the most DB-expensive routes table controls.
 *
 * @param {HTMLElement} section
 *   The database section.
 */
function initRoutesTable(section) {
  initInteractiveTable({
    table: section.querySelector(".no-db-routes-table"),
    searchInput: section.querySelector(".no-db-routes-table-search"),
    emptyState: section.querySelector(".no-db-routes-table-empty-state"),
    prevButton: section.querySelector(".no-db-routes-pagination__prev"),
    nextButton: section.querySelector(".no-db-routes-pagination__next"),
    status: section.querySelector(".no-db-routes-pagination__status"),
    exportButton: section.querySelector(".no-db-routes-export-csv"),
    exportFilename: "database-most-db-expensive-routes.csv",
    defaultSortIndex: 1,
    defaultSortDirection: "desc",
  });
}

/**
 * Initializes the latest records table controls.
 *
 * @param {HTMLElement} section
 *   The database section.
 */
function initRecordsTable(section) {
  initInteractiveTable({
    table: section.querySelector(".no-db-records-table"),
    searchInput: section.querySelector(".no-db-table-search"),
    emptyState: section.querySelector(".no-db-table-empty-state"),
    prevButton: section.querySelector(".no-db-pagination__prev"),
    nextButton: section.querySelector(".no-db-pagination__next"),
    status: section.querySelector(".no-db-pagination__status"),
    exportButton: section.querySelector(".no-db-export-csv"),
    exportFilename: "database-slow-query-records.csv",
    defaultSortIndex: 0,
    defaultSortDirection: "desc",
  });
}

/**
 * Initializes the pressure trend chart.
 *
 * @param {HTMLElement} host
 *   The host section.
 * @param {Object} chartSettings
 *   The chart settings.
 */
function initPressureTrendChart(host, chartSettings) {
  const chartId = host.dataset.pressureTrendChartId || "";
  const chartElement = chartId ? host.querySelector(`#${chartId}`) : null;

  if (!chartElement) {
    return;
  }

  const settings = chartSettings[chartId] || {};
  const hasData =
    Array.isArray(settings.series) &&
    settings.series.some(
      (item) => Array.isArray(item.data) && item.data.length,
    );

  if (!hasData) {
    createChartFallback(
      chartElement.parentNode || chartElement,
      host.dataset.chartEmptyMessage || "",
    );
    return;
  }

  initResponsiveEchart(
    chartElement,
    buildPressureTrendOption(host, settings),
    host.dataset.chartLibraryMessage || "",
  );
}

/**
 * Initializes the request comparison chart.
 *
 * @param {HTMLElement} host
 *   The host section.
 * @param {Object} chartSettings
 *   The chart settings.
 */
function initRequestComparisonChart(host, chartSettings) {
  const chartId = host.dataset.requestComparisonChartId || "";
  const chartElement = chartId ? host.querySelector(`#${chartId}`) : null;

  if (!chartElement) {
    return;
  }

  const settings = chartSettings[chartId] || {};
  const hasData = Array.isArray(settings.points) && settings.points.length > 0;

  if (!hasData) {
    createChartFallback(
      chartElement.parentNode || chartElement,
      host.dataset.chartEmptyMessage || "",
    );
    return;
  }

  initResponsiveEchart(
    chartElement,
    buildRequestComparisonOption(host, settings),
    host.dataset.chartLibraryMessage || "",
  );
}

/**
 * Initializes the database observability section.
 *
 * @param {HTMLElement} container
 *   The dashboard root element.
 * @param {Object} chartSettings
 *   The database chart settings.
 */
export default function initDatabaseObservability(container, chartSettings) {
  const sections = container.querySelectorAll(".no-database-observability");

  if (!sections.length) {
    return;
  }

  sections.forEach((section) => {
    if (section.dataset.chartReady === "true") {
      return;
    }

    initPressureTrendChart(section, chartSettings);
    initRequestComparisonChart(section, chartSettings);
    initDatabaseModal(section);
    initRoutesTable(section);
    initRecordsTable(section);

    section.dataset.chartReady = "true";
  });
}
