<template>
  <div ref="controlRef" class="table-control-bar">
    <label class="table-search-field">
      <span>{{ searchLabel }}</span>
      <input :value="search" type="search" :placeholder="placeholder" @input="emit('update:search', $event.target.value)" />
    </label>
    <div class="table-view-mode-switch" role="group" aria-label="表格展示模式">
      <button
        type="button"
        class="table-view-mode-button"
        :class="{ 'table-view-mode-active': tableMode === 'overview' }"
        :aria-pressed="tableMode === 'overview'"
        @click="setTableMode('overview')"
      >
        概览模式
      </button>
      <button
        type="button"
        class="table-view-mode-button"
        :class="{ 'table-view-mode-active': tableMode === 'wide' }"
        :aria-pressed="tableMode === 'wide'"
        @click="setTableMode('wide')"
      >
        宽表模式
      </button>
    </div>
  </div>
</template>

<script setup>
import { nextTick, onBeforeUnmount, onMounted, ref, watch } from "vue";

import { normalizeTableViewMode, readStoredTableMode, TABLE_VIEW_MODE_STORAGE_KEY } from "../utils/tableViewMode";

const props = defineProps({
  search: {
    type: String,
    default: ""
  },
  searchLabel: {
    type: String,
    default: "搜索"
  },
  placeholder: {
    type: String,
    default: "输入编码、名称、客户、单号等关键词"
  },
  sortKey: {
    type: String,
    default: ""
  },
  sortDirection: {
    type: String,
    default: "desc"
  },
  sortOptions: {
    type: Array,
    default: () => []
  }
});

const emit = defineEmits(["update:search", "update:sortKey", "update:sortDirection"]);

function normalizeTableMode(value) {
  return normalizeTableViewMode(value);
}

const controlRef = ref(null);
const tableMode = ref(readStoredTableMode());
let enhancedTable = null;
let modeTable = null;
let modeTableObserver = null;
let tooltipTable = null;
let tooltipElement = null;
let tooltipCleanupHandlers = [];
let cleanupHandlers = [];
let headerObserver = null;
let enhanceFrame = 0;
let modeFrame = 0;
let isEnhancing = false;

function normalizeLabel(value) {
  return String(value || "")
    .replace(/\s+/g, "")
    .toLowerCase();
}

function findTableAfterControl() {
  const control = controlRef.value;
  if (!control) {
    return null;
  }

  let sibling = control.nextElementSibling;
  while (sibling) {
    if (sibling.matches?.("table")) {
      return sibling;
    }

    const nestedTable = sibling.querySelector?.("table");
    if (nestedTable) {
      return nestedTable;
    }

    sibling = sibling.nextElementSibling;
  }

  const scope =
    control.closest(".panel, .workflow-drawer, .workflow-section-card, .workflow-summary-card, .compact-side-drawer") ||
    control.parentElement;
  const tables = Array.from(scope?.querySelectorAll("table") || []);
  return tables.find((table) => control.compareDocumentPosition(table) & Node.DOCUMENT_POSITION_FOLLOWING) || null;
}

function getTableViewWrap(table) {
  return (
    table?.closest?.(".table-wrap, .drawer-data-table-wrap, .special-adjustment-table-wrap") ||
    table?.parentElement ||
    null
  );
}

function clearTableViewClasses(table) {
  if (!table) {
    return;
  }

  const wrap = getTableViewWrap(table);
  table.classList.remove("data-table-overview", "data-table-wide");
  wrap?.classList.remove("table-view-overview", "table-view-wide");
  delete table.dataset.tableViewMode;
}

function disconnectModeObserver() {
  if (modeTableObserver) {
    modeTableObserver.disconnect();
    modeTableObserver = null;
  }
}

function hideCellTooltip() {
  tooltipElement?.classList.remove("table-cell-hover-tooltip-visible");
}

function ensureTooltipElement() {
  if (typeof document === "undefined") {
    return null;
  }

  tooltipElement = document.querySelector(".table-cell-hover-tooltip");
  if (!tooltipElement) {
    tooltipElement = document.createElement("div");
    tooltipElement.className = "table-cell-hover-tooltip";
    tooltipElement.setAttribute("role", "tooltip");
    document.body.appendChild(tooltipElement);
  }

  return tooltipElement;
}

function positionCellTooltip(event) {
  if (!tooltipElement) {
    return;
  }

  const padding = 14;
  const offset = 16;
  const rect = tooltipElement.getBoundingClientRect();
  const nextLeft = Math.min(event.clientX + offset, window.innerWidth - rect.width - padding);
  const nextTop = Math.min(event.clientY + offset, window.innerHeight - rect.height - padding);

  tooltipElement.style.left = `${Math.max(padding, nextLeft)}px`;
  tooltipElement.style.top = `${Math.max(padding, nextTop)}px`;
}

function getTooltipCell(event) {
  const cell = event.target?.closest?.('td[data-table-tooltip="auto"]');
  if (!cell || !tooltipTable?.contains(cell)) {
    return null;
  }

  return cell;
}

function showCellTooltip(cell, event) {
  const value = cell.dataset.tableFullValue || "";
  if (!value || !["overview", "wide"].includes(tableMode.value)) {
    hideCellTooltip();
    return;
  }

  const tooltip = ensureTooltipElement();
  if (!tooltip) {
    return;
  }

  tooltip.textContent = value;
  tooltip.classList.add("table-cell-hover-tooltip-visible");
  positionCellTooltip(event);
}

function clearTooltipListeners() {
  tooltipCleanupHandlers.forEach((cleanup) => cleanup());
  tooltipCleanupHandlers = [];
  tooltipTable = null;
  hideCellTooltip();
}

function bindCellTooltip(table) {
  if (!table || tooltipTable === table) {
    return;
  }

  clearTooltipListeners();
  tooltipTable = table;

  const handleMouseOver = (event) => {
    const cell = getTooltipCell(event);
    if (cell) {
      showCellTooltip(cell, event);
    }
  };
  const handleMouseMove = (event) => {
    const cell = getTooltipCell(event);
    if (cell) {
      showCellTooltip(cell, event);
    }
  };
  const handleMouseOut = (event) => {
    const cell = getTooltipCell(event);
    if (cell && !cell.contains(event.relatedTarget)) {
      hideCellTooltip();
    }
  };
  const handleDocumentMouseMove = (event) => {
    if (!getTooltipCell(event)) {
      hideCellTooltip();
    }
  };

  table.addEventListener("mouseover", handleMouseOver);
  table.addEventListener("mousemove", handleMouseMove);
  table.addEventListener("mouseout", handleMouseOut);
  document.addEventListener("mousemove", handleDocumentMouseMove, true);
  window.addEventListener("scroll", hideCellTooltip, true);
  window.addEventListener("blur", hideCellTooltip);
  tooltipCleanupHandlers.push(() => table.removeEventListener("mouseover", handleMouseOver));
  tooltipCleanupHandlers.push(() => table.removeEventListener("mousemove", handleMouseMove));
  tooltipCleanupHandlers.push(() => table.removeEventListener("mouseout", handleMouseOut));
  tooltipCleanupHandlers.push(() => document.removeEventListener("mousemove", handleDocumentMouseMove, true));
  tooltipCleanupHandlers.push(() => window.removeEventListener("scroll", hideCellTooltip, true));
  tooltipCleanupHandlers.push(() => window.removeEventListener("blur", hideCellTooltip));
}

function refreshCellTooltips(table) {
  const shouldShowCellTooltip = ["overview", "wide"].includes(tableMode.value);
  const cells = Array.from(table?.tBodies || []).flatMap((tbody) => Array.from(tbody.querySelectorAll("td")));

  cells.forEach((cell) => {
    const value = cell.innerText?.trim() || cell.textContent?.trim() || "";
    const hasBlockingInteractiveContent = Boolean(
      cell.querySelector("input, select, textarea, a, button:not(.link-button), button.semantic-action-button")
    );

    if (shouldShowCellTooltip && value && !hasBlockingInteractiveContent) {
      cell.dataset.tableTooltip = "auto";
      cell.dataset.tableFullValue = value;
      if (!cell.getAttribute("aria-label") || cell.dataset.tableAria === "auto") {
        cell.setAttribute("aria-label", value);
        cell.dataset.tableAria = "auto";
      }
      cell.removeAttribute("title");
      return;
    }

    if (cell.dataset.tableTooltip === "auto") {
      cell.removeAttribute("title");
      delete cell.dataset.tableTooltip;
      delete cell.dataset.tableFullValue;
      if (cell.dataset.tableAria === "auto") {
        cell.removeAttribute("aria-label");
        delete cell.dataset.tableAria;
      }
    }
  });
}

function observeTableForViewMode(table) {
  if (modeTableObserver || !table) {
    return;
  }

  modeTableObserver = new MutationObserver(() => {
    scheduleApplyTableViewMode();
  });
  modeTableObserver.observe(table, { childList: true, characterData: true, subtree: true });
}

function applyTableViewMode(table = findTableAfterControl()) {
  if (!table) {
    return;
  }

  if (modeTable && modeTable !== table) {
    clearTableViewClasses(modeTable);
    disconnectModeObserver();
  }

  modeTable = table;
  const wrap = getTableViewWrap(table);
  const isWide = tableMode.value === "wide";

  table.classList.toggle("data-table-overview", !isWide);
  table.classList.toggle("data-table-wide", isWide);
  table.dataset.tableViewMode = isWide ? "wide" : "overview";

  wrap?.classList.toggle("table-view-overview", !isWide);
  wrap?.classList.toggle("table-view-wide", isWide);

  refreshCellTooltips(table);
  bindCellTooltip(table);
  observeTableForViewMode(table);
}

function scheduleApplyTableViewMode() {
  if (modeFrame) {
    return;
  }

  modeFrame = window.requestAnimationFrame(() => {
    modeFrame = 0;
    applyTableViewMode();
  });
}

function setTableMode(mode) {
  const nextMode = normalizeTableMode(mode);
  if (tableMode.value === nextMode) {
    return;
  }

  tableMode.value = nextMode;
}

function getOriginalHeaderLabel(th) {
  return th.dataset.originalHeaderLabel || th.textContent.trim();
}

function findSortOption(label) {
  const normalizedHeader = normalizeLabel(label);
  if (!normalizedHeader) {
    return null;
  }

  const scoredOptions = props.sortOptions
    .map((option) => {
      const normalizedOptionLabel = normalizeLabel(option.label);
      const normalizedOptionKey = normalizeLabel(option.key);
      let score = 0;

      if (normalizedOptionLabel === normalizedHeader) {
        score = 100;
      } else if (normalizedOptionKey === normalizedHeader) {
        score = 96;
      } else if (normalizedHeader === "编码" && normalizedOptionLabel.endsWith("编码")) {
        score = 90;
      } else if (normalizedHeader === "名称" && normalizedOptionLabel.endsWith("名称")) {
        score = 90;
      } else if (normalizedOptionLabel === `${normalizedHeader}名称`) {
        score = 86;
      } else if (normalizedOptionLabel === `${normalizedHeader}编码`) {
        score = 82;
      } else if (normalizedOptionLabel.startsWith(normalizedHeader) && normalizedOptionLabel.endsWith("名称")) {
        score = 78;
      } else if (normalizedOptionLabel.startsWith(normalizedHeader) && normalizedOptionLabel.endsWith("编码")) {
        score = 68;
      } else if (normalizedOptionLabel.includes(normalizedHeader)) {
        score = 58;
      } else if (normalizedHeader.includes(normalizedOptionLabel)) {
        score = 52;
      }

      return { option, score };
    })
    .filter((item) => item.score > 0)
    .sort((left, right) => right.score - left.score);

  return scoredOptions[0]?.option || null;
}

function disconnectHeaderObserver() {
  if (headerObserver) {
    headerObserver.disconnect();
    headerObserver = null;
  }
}

function clearEnhancedHeaders({ disconnectObserver = true } = {}) {
  if (disconnectObserver) {
    disconnectHeaderObserver();
  }

  cleanupHandlers.forEach((cleanup) => cleanup());
  cleanupHandlers = [];

  if (!enhancedTable) {
    return;
  }

  enhancedTable.querySelectorAll("th.sortable-th").forEach((th) => {
    const hasEnhancedButton = Boolean(th.querySelector(".sortable-th-button"));
    const currentHeaderLabel = th.textContent.trim();
    const originalLabel = hasEnhancedButton ? th.dataset.originalHeaderLabel : currentHeaderLabel || th.dataset.originalHeaderLabel;
    th.classList.remove("sortable-th", "sortable-th-active", "sortable-th-asc", "sortable-th-desc");
    delete th.dataset.sortKey;
    delete th.dataset.originalHeaderLabel;
    if (originalLabel) {
      th.textContent = originalLabel;
    }
  });
  enhancedTable = null;
}

function scheduleEnhanceHeaders() {
  if (isEnhancing || enhanceFrame) {
    return;
  }

  enhanceFrame = window.requestAnimationFrame(() => {
    enhanceFrame = 0;
    enhanceTableHeaders();
  });
}

function observeTableHead(table) {
  disconnectHeaderObserver();

  if (!table?.tHead) {
    return;
  }

  headerObserver = new MutationObserver(() => {
    scheduleEnhanceHeaders();
  });
  headerObserver.observe(table.tHead, { childList: true, characterData: true, subtree: true });
}

function toggleSort(sortKey) {
  if (props.sortKey === sortKey) {
    emit("update:sortDirection", props.sortDirection === "asc" ? "desc" : "asc");
    return;
  }

  emit("update:sortKey", sortKey);
  emit("update:sortDirection", "asc");
}

function refreshHeaderStates() {
  const table = enhancedTable || findTableAfterControl();
  if (!table) {
    return;
  }

  table.querySelectorAll("th.sortable-th").forEach((th) => {
    const isActive = th.dataset.sortKey === props.sortKey;
    const arrow = th.querySelector(".sort-arrow");
    th.classList.toggle("sortable-th-active", isActive);
    th.classList.toggle("sortable-th-asc", isActive && props.sortDirection === "asc");
    th.classList.toggle("sortable-th-desc", isActive && props.sortDirection === "desc");

    if (arrow) {
      arrow.textContent = isActive ? (props.sortDirection === "asc" ? "↑" : "↓") : "";
      arrow.classList.toggle("sort-arrow-active", isActive);
    }
  });
}

function enhanceTableHeaders() {
  isEnhancing = true;
  clearEnhancedHeaders();

  const table = findTableAfterControl();
  applyTableViewMode(table);

  if (!props.sortOptions.length) {
    isEnhancing = false;
    return;
  }

  const headerRows = Array.from(table?.tHead?.rows || []);
  if (!table || !headerRows.length) {
    isEnhancing = false;
    return;
  }

  enhancedTable = table;

  headerRows
    .flatMap((row) => Array.from(row.cells))
    .filter((th) => th.tagName === "TH" && Number(th.colSpan || 1) === 1)
    .forEach((th) => {
      const originalLabel = getOriginalHeaderLabel(th);
      const option = findSortOption(originalLabel);
      if (!option) {
        return;
      }

      th.dataset.originalHeaderLabel = originalLabel;
      th.dataset.sortKey = option.key;
      th.classList.add("sortable-th");
      th.textContent = "";

      const button = document.createElement("button");
      button.type = "button";
      button.className = "sortable-th-button";
      button.setAttribute("aria-label", `按${option.label}排序`);

      const label = document.createElement("span");
      label.className = "sortable-th-label";
      label.textContent = originalLabel;

      const arrow = document.createElement("span");
      arrow.className = "sort-arrow";
      arrow.setAttribute("aria-hidden", "true");
      arrow.textContent = "";

      button.append(label, arrow);

      const listener = () => toggleSort(option.key);
      button.addEventListener("click", listener);
      cleanupHandlers.push(() => button.removeEventListener("click", listener));

      th.appendChild(button);
    });

  refreshHeaderStates();
  observeTableHead(table);
  isEnhancing = false;
}

onMounted(() => {
  nextTick(enhanceTableHeaders);
});

watch(
  () => props.sortOptions,
  () => nextTick(enhanceTableHeaders),
  { deep: true }
);

watch(
  () => [props.sortKey, props.sortDirection],
  () => nextTick(refreshHeaderStates)
);

watch(
  tableMode,
  (mode) => {
    if (typeof window !== "undefined") {
      window.localStorage.setItem(TABLE_VIEW_MODE_STORAGE_KEY, mode);
    }
    nextTick(applyTableViewMode);
  }
);

onBeforeUnmount(() => {
  if (enhanceFrame) {
    window.cancelAnimationFrame(enhanceFrame);
    enhanceFrame = 0;
  }
  if (modeFrame) {
    window.cancelAnimationFrame(modeFrame);
    modeFrame = 0;
  }
  clearTableViewClasses(modeTable);
  disconnectModeObserver();
  clearTooltipListeners();
  clearEnhancedHeaders();
});
</script>
