<script setup lang="ts">
import { computed, h, ref } from "vue";
import {
  useVueTable,
  getCoreRowModel,
  getFilteredRowModel,
  getSortedRowModel,
  createColumnHelper,
  FlexRender,
  type SortingState,
  type ColumnOrderState,
  type Updater,
} from "@tanstack/vue-table";
import { storeToRefs } from "pinia";
import GmWindow from "./GmWindow.vue";
import GmPlayerFactionPanel from "./GmPlayerFactionPanel.vue";
import GmGeopoliticsPanel from "./GmGeopoliticsPanel.vue";
import { useGmStore } from "../../ui/stores/gmStore";
import { usePlayerFactionStore } from "../../ui/stores/playerFactionStore";
import { useViewerSelectionStore } from "../../ui/stores/viewerSelection";
import type { ShipSnapshot } from "../../contractsShips";
import type { StationSnapshot } from "../../contractsInfrastructure";
import type { FactionSnapshot } from "../../contractsFactions";
import type { MarketOrderSnapshot } from "../../contractsEconomy";

// ── Column ordering composable ─────────────────────────────────────────────

function useColumnOrder(initialIds: string[]) {
  const columnOrder = ref<ColumnOrderState>([...initialIds]);
  const draggingId = ref<string | null>(null);
  const overId = ref<string | null>(null);

  function onColumnOrderChange(updater: Updater<ColumnOrderState>) {
    columnOrder.value = typeof updater === "function" ? updater(columnOrder.value) : updater;
  }

  function onDragStart(e: DragEvent, id: string) {
    draggingId.value = id;
    if (e.dataTransfer) {
      e.dataTransfer.effectAllowed = "move";
      e.dataTransfer.setData("text/plain", id);
    }
  }

  function onDragOver(e: DragEvent, id: string) {
    e.preventDefault();
    if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
    overId.value = id;
  }

  function onDragLeave() {
    overId.value = null;
  }

  function onDrop(targetId: string) {
    if (!draggingId.value || draggingId.value === targetId) return;
    const order = [...columnOrder.value];
    const from = order.indexOf(draggingId.value);
    const to = order.indexOf(targetId);
    order.splice(from, 1);
    order.splice(to, 0, draggingId.value);
    columnOrder.value = order;
    draggingId.value = null;
    overId.value = null;
  }

  function onDragEnd() {
    draggingId.value = null;
    overId.value = null;
  }

  return { columnOrder, draggingId, overId, onColumnOrderChange, onDragStart, onDragOver, onDragLeave, onDrop, onDragEnd };
}

const emit = defineEmits<{
  close: [];
  focus: [id: string, kind: "ship" | "station"];
}>();

type TabId = "ships" | "stations" | "factions" | "player" | "geopolitics";
const activeTab = ref<TabId>("ships");

const gmStore = useGmStore();
const playerFactionStore = usePlayerFactionStore();
const selectionStore = useViewerSelectionStore();
const { selectedEntityId, selectedEntityKind } = storeToRefs(selectionStore);

// Faction name lookup
const factionMap = computed(() =>
  new Map(gmStore.factions.map((f) => [f.id, f.label])),
);

const factionColorMap = computed(() =>
  new Map(gmStore.factions.map((f) => [f.id, f.color])),
);

function renderColorCell(color: string | null | undefined) {
  const resolved = color && color !== "—" ? color : "#6b7280";
  return h("div", { class: "flex items-center justify-center" }, [
    h("span", {
      class: "inline-block h-3 w-3 rounded-full border border-white/20",
      style: { backgroundColor: resolved },
    }),
  ]);
}

function titleCaseToken(value: string | null | undefined) {
  if (!value) return "—";
  return value
    .replace(/([a-z])([A-Z])/g, "$1 $2")
    .replace(/[-_]+/g, " ")
    .replace(/\s+/g, " ")
    .trim()
    .replace(/\b\w/g, (c) => c.toUpperCase());
}

function compactNumber(value: number | null | undefined, digits = 0) {
  if (value == null || Number.isNaN(value)) return "—";
  return value.toFixed(digits);
}

function compactRate(value: number | null | undefined) {
  if (value == null || Number.isNaN(value)) return "—";
  const sign = value > 0.001 ? "+" : "";
  return `${sign}${value.toFixed(2)}/s`;
}

function formatCargoAmount(value: number | null | undefined) {
  if (value == null || Number.isNaN(value)) return "—";
  const rounded = Math.round(value);
  if (Math.abs(value - rounded) < 0.005) return String(rounded);
  return value.toFixed(2).replace(/\.?0+$/, "");
}

function formatPercent(value: number | null | undefined) {
  if (value == null || Number.isNaN(value)) return "—";
  return `${Math.round(value * 100)}%`;
}

function getLeadCampaign(faction: FactionSnapshot) {
  return [...faction.strategicState.campaigns]
    .sort((left, right) => right.priority - left.priority)
    .find((campaign) => campaign.status !== "completed" && campaign.status !== "cancelled")
    ?? faction.strategicState.campaigns[0];
}

function getLeadObjective(faction: FactionSnapshot) {
  return [...faction.strategicState.objectives]
    .sort((left, right) => right.priority - left.priority)
    .find((objective) => objective.status !== "completed" && objective.status !== "cancelled")
    ?? faction.strategicState.objectives[0];
}

function getLatestDecision(faction: FactionSnapshot) {
  return [...faction.decisionLog]
    .sort((left, right) => right.occurredAtUtc.localeCompare(left.occurredAtUtc))[0];
}

function describeCommodityState(faction: FactionSnapshot, itemId: string, shortLabel: string) {
  const signal = faction.strategicState.economicAssessment.commoditySignals.find((entry) => entry.itemId === itemId);
  if (!signal) return `${shortLabel} —`;
  return `${shortLabel} ${titleCaseToken(signal.level)} ${compactRate(signal.projectedNetRatePerSecond)}`;
}

function describeFactionStrategicState(faction: FactionSnapshot) {
  const campaign = getLeadCampaign(faction);
  const objective = getLeadObjective(faction);
  if (!campaign && !objective) return "No campaigns";
  if (!campaign) return `${titleCaseToken(objective?.kind)} · ${titleCaseToken(objective?.status)}`;
  return `${titleCaseToken(campaign.kind)} · ${titleCaseToken(campaign.status)}`;
}

function describeFactionLeadTask(faction: FactionSnapshot) {
  const objective = getLeadObjective(faction);
  if (!objective) return "No objectives";
  const target = objective.itemId ?? objective.targetEntityId ?? objective.targetSystemId ?? objective.homeStationId;
  return target
    ? `${titleCaseToken(objective.kind)} · ${titleCaseToken(objective.status)} · ${target}`
    : `${titleCaseToken(objective.kind)} · ${titleCaseToken(objective.status)}`;
}

function describeFactionEconomy(faction: FactionSnapshot) {
  return [
    describeCommodityState(faction, "refinedmetals", "RM"),
    describeCommodityState(faction, "hullparts", "HP"),
    describeCommodityState(faction, "claytronics", "CL"),
  ].join(" | ");
}

function describeFactionThreat(faction: FactionSnapshot) {
  const threat = faction.strategicState.threatAssessment;
  return `Enemy ships ${threat.enemyShipCount} · stations ${threat.enemyStationCount}`;
}

function describeFactionCommitments(faction: FactionSnapshot) {
  const economic = faction.strategicState.economicAssessment;
  return `Mil ${economic.militaryShipCount}/${economic.targetMilitaryShipCount} · Min ${economic.minerShipCount}/${economic.targetMinerShipCount} · Tr ${economic.transportShipCount}/${economic.targetTransportShipCount}`;
}

function describeFactionReserves(faction: FactionSnapshot) {
  const budget = faction.strategicState.budget;
  return `Assets ${budget.reservedMilitaryAssets}/${budget.reservedLogisticsAssets}/${budget.reservedConstructionAssets} · Credits ${compactNumber(budget.reservedCredits, 0)}`;
}

function describeFactionBottleneck(faction: FactionSnapshot) {
  const economic = faction.strategicState.economicAssessment;
  if (!economic.industrialBottleneckItemId) {
    return `None · sustain ${formatPercent(economic.sustainmentScore)}`;
  }
  return `${economic.industrialBottleneckItemId} · sustain ${formatPercent(economic.sustainmentScore)} · replace ${formatPercent(economic.replacementPressure)}`;
}

function describeFactionIntent(faction: FactionSnapshot) {
  const latestDecision = getLatestDecision(faction);
  const leadCampaign = getLeadCampaign(faction);
  if (!leadCampaign) return latestDecision?.summary ?? "—";
  const pause = leadCampaign.pauseReason ? ` · ${leadCampaign.pauseReason}` : "";
  return `${titleCaseToken(leadCampaign.kind)} · ${titleCaseToken(leadCampaign.status)}${pause}`;
}

function describeFactionMemory(faction: FactionSnapshot) {
  const topSystem = [...faction.memory.systems]
    .sort((left, right) => (right.frontierPressure + right.routeRisk + right.historicalShortagePressure)
      - (left.frontierPressure + left.routeRisk + left.historicalShortagePressure))[0];
  const topCommodity = [...faction.memory.commodities]
    .sort((left, right) => right.historicalShortageScore - left.historicalShortageScore)[0];
  if (!topSystem && !topCommodity) return "—";
  return `${topSystem ? `${topSystem.systemId} fp ${compactNumber(topSystem.frontierPressure, 1)}` : "no-front"}${topCommodity ? ` · ${topCommodity.itemId} hs ${compactNumber(topCommodity.historicalShortageScore, 1)}` : ""}`;
}

function describeFactionDecision(faction: FactionSnapshot) {
  const latestDecision = getLatestDecision(faction);
  return latestDecision ? `${titleCaseToken(latestDecision.kind)} · ${latestDecision.summary}` : "—";
}

function describeFactionFronts(faction: FactionSnapshot) {
  const activeTheaters = faction.strategicState.theaters.filter((theater) => theater.status === "active");
  const defense = activeTheaters.filter((theater) => theater.kind.includes("defense")).length;
  const offense = activeTheaters.filter((theater) => theater.kind.includes("offense")).length;
  const economy = activeTheaters.filter((theater) => theater.kind.includes("economic")).length;
  return `${activeTheaters.length} active · D ${defense} · O ${offense} · E ${economy}`;
}

// ── Ships table ────────────────────────────────────────────────────────────

type ShipRow = {
  id: string;
  label: string;
  class: string;
  factionColor: string;
  faction: string;
  system: string;
  state: string;
  assignment: string;
  behavior: string;
  orders: string;
  plan: string;
  step: string;
  subtask: string;
  cargo: number;
  health: number;
};

const shipRows = computed<ShipRow[]>(() =>
  gmStore.ships.map((s) => {
    const topOrder = [...s.orderQueue]
      .sort((left, right) => right.priority - left.priority || left.createdAtUtc.localeCompare(right.createdAtUtc))[0];
    const currentStep = s.activePlan?.steps[s.activePlan.currentStepIndex];
    const currentSubTask = s.activeSubTasks[0];
    return {
      id: s.id,
      label: s.label,
      class: s.class,
      factionColor: factionColorMap.value.get(s.factionId) ?? "—",
      faction: factionMap.value.get(s.factionId) ?? s.factionId,
      system: s.systemId,
      state: titleCaseToken(s.state),
      assignment: s.assignment ? titleCaseToken(s.assignment.kind) : "—",
      behavior: titleCaseToken(s.defaultBehavior.kind),
      orders: topOrder ? `${titleCaseToken(topOrder.kind)} · ${s.orderQueue.length}` : "—",
      plan: s.activePlan ? `${titleCaseToken(s.activePlan.kind)} · ${titleCaseToken(s.activePlan.status)}` : "—",
      step: currentStep ? `${titleCaseToken(currentStep.kind)} · ${titleCaseToken(currentStep.status)}` : "—",
      subtask: currentSubTask ? `${titleCaseToken(currentSubTask.kind)} ${Math.round(currentSubTask.progress * 100)}%` : "—",
      cargo: s.inventory.reduce((sum, e) => sum + e.amount, 0),
      health: Math.round(s.health),
    };
  }),
);

const shipColumnHelper = createColumnHelper<ShipRow>();
const shipColumns = [
  shipColumnHelper.accessor("label", { header: "Name" }),
  shipColumnHelper.accessor("class", { header: "Class" }),
  shipColumnHelper.accessor("factionColor", {
    id: "factionColor",
    header: "Color",
    cell: (info) => renderColorCell(info.getValue()),
  }),
  shipColumnHelper.accessor("faction", { header: "Faction" }),
  shipColumnHelper.accessor("system", { header: "System" }),
  shipColumnHelper.accessor("state", { header: "Ship State" }),
  shipColumnHelper.accessor("assignment", { header: "Assignment" }),
  shipColumnHelper.accessor("behavior", { header: "Behavior" }),
  shipColumnHelper.accessor("orders", { header: "Orders" }),
  shipColumnHelper.accessor("plan", { header: "Plan" }),
  shipColumnHelper.accessor("step", { header: "Current Step" }),
  shipColumnHelper.accessor("subtask", { header: "SubTask" }),
  shipColumnHelper.accessor("cargo", {
    header: "Cargo",
    cell: (info) => formatCargoAmount(info.getValue()),
  }),
  shipColumnHelper.accessor("health", { header: "HP" }),
];

const shipFilter = ref("");
const shipSorting = ref<SortingState>([]);
const shipOrder = useColumnOrder(["label", "class", "factionColor", "faction", "system", "state", "assignment", "behavior", "orders", "plan", "step", "subtask", "cargo", "health"]);

const shipTable = useVueTable({
  get data() { return shipRows.value; },
  columns: shipColumns,
  state: {
    get globalFilter() { return shipFilter.value; },
    get sorting() { return shipSorting.value; },
    get columnOrder() { return shipOrder.columnOrder.value; },
  },
  onGlobalFilterChange: (v) => { shipFilter.value = String(v); },
  onSortingChange: (updater) => {
    shipSorting.value = typeof updater === "function" ? updater(shipSorting.value) : updater;
  },
  onColumnOrderChange: shipOrder.onColumnOrderChange,
  getCoreRowModel: getCoreRowModel(),
  getFilteredRowModel: getFilteredRowModel(),
  getSortedRowModel: getSortedRowModel(),
});

// ── Stations table ─────────────────────────────────────────────────────────

type StationRow = {
  id: string;
  label: string;
  category: string;
  objective: string;
  factionColor: string;
  faction: string;
  system: string;
  process: string;
  workforce: string;
  docked: string;
  orders: number;
  orderDetails: MarketOrderSnapshot[];
  cargo: number;
  modules: number;
};

const marketOrderMap = computed(() =>
  new Map(gmStore.marketOrders.map((o) => [o.id, o])),
);

const stationRows = computed<StationRow[]>(() =>
  gmStore.stations.map((s) => ({
    id: s.id,
    label: s.label,
    category: s.category,
    objective: titleCaseToken(s.objective),
    factionColor: factionColorMap.value.get(s.factionId) ?? "—",
    faction: factionMap.value.get(s.factionId) ?? s.factionId,
    system: s.systemId,
    process: s.currentProcesses.length > 0
      ? s.currentProcesses.map((process) => `${process.label} ${Math.round(process.progress * 100)}%`).join(" | ")
      : "Idle",
    workforce: `${Math.round(s.population)} / ${Math.round(s.populationCapacity)} · ${Math.round(s.workforceEffectiveRatio * 100)}%`,
    docked: `${s.dockedShips} / ${s.dockingPads}`,
    orders: s.marketOrderIds.length,
    orderDetails: s.marketOrderIds.flatMap((id) => {
      const order = marketOrderMap.value.get(id);
      return order ? [order] : [];
    }),
    cargo: s.inventory.reduce((sum, e) => sum + e.amount, 0),
    modules: s.installedModules.length,
  })),
);

const stationColumnHelper = createColumnHelper<StationRow>();
const stationColumns = [
  stationColumnHelper.accessor("label", { header: "Name" }),
  stationColumnHelper.accessor("category", { header: "Category" }),
  stationColumnHelper.accessor("objective", { header: "Objective" }),
  stationColumnHelper.accessor("factionColor", {
    id: "factionColor",
    header: "Color",
    cell: (info) => renderColorCell(info.getValue()),
  }),
  stationColumnHelper.accessor("faction", { header: "Faction" }),
  stationColumnHelper.accessor("system", { header: "System" }),
  stationColumnHelper.accessor("process", { header: "Production" }),
  stationColumnHelper.accessor("workforce", { header: "Workforce" }),
  stationColumnHelper.accessor("docked", { header: "Docked" }),
  stationColumnHelper.accessor("orders", { header: "Orders" }),
  stationColumnHelper.accessor("cargo", {
    header: "Cargo",
    cell: (info) => formatCargoAmount(info.getValue()),
  }),
  stationColumnHelper.accessor("modules", { header: "Modules" }),
];

const stationFilter = ref("");
const stationSorting = ref<SortingState>([]);
const stationOrder = useColumnOrder(["label", "category", "objective", "factionColor", "faction", "system", "process", "workforce", "docked", "orders", "cargo", "modules"]);

const stationTable = useVueTable({
  get data() { return stationRows.value; },
  columns: stationColumns,
  state: {
    get globalFilter() { return stationFilter.value; },
    get sorting() { return stationSorting.value; },
    get columnOrder() { return stationOrder.columnOrder.value; },
  },
  onGlobalFilterChange: (v) => { stationFilter.value = String(v); },
  onSortingChange: (updater) => {
    stationSorting.value = typeof updater === "function" ? updater(stationSorting.value) : updater;
  },
  onColumnOrderChange: stationOrder.onColumnOrderChange,
  getCoreRowModel: getCoreRowModel(),
  getFilteredRowModel: getFilteredRowModel(),
  getSortedRowModel: getSortedRowModel(),
});

// ── Factions table ─────────────────────────────────────────────────────────

type FactionRow = {
  id: string;
  label: string;
  color: string;
  planCycle: number;
  posture: string;
  fronts: string;
  leadCampaign: string;
  leadObjective: string;
  commitments: string;
  reserves: string;
  bottleneck: string;
  intent: string;
  decision: string;
  memory: string;
  economy: string;
  threat: string;
  systems: string;
  credits: number;
  population: number;
  shipsBuilt: number;
  shipsLost: number;
};

const factionRows = computed<FactionRow[]>(() =>
  gmStore.factions.map((f) => ({
    id: f.id,
    label: f.label,
    color: f.color,
    planCycle: f.strategicState.planCycle,
    posture: `${titleCaseToken(f.doctrine.strategicPosture)} · ${titleCaseToken(f.doctrine.militaryPosture)} · ${titleCaseToken(f.doctrine.economicPosture)}`,
    fronts: describeFactionFronts(f),
    leadCampaign: describeFactionStrategicState(f),
    leadObjective: describeFactionLeadTask(f),
    commitments: describeFactionCommitments(f),
    reserves: describeFactionReserves(f),
    bottleneck: describeFactionBottleneck(f),
    intent: describeFactionIntent(f),
    decision: describeFactionDecision(f),
    memory: describeFactionMemory(f),
    economy: describeFactionEconomy(f),
    threat: describeFactionThreat(f),
    systems: `${f.strategicState.economicAssessment.controlledSystemCount} / ${f.doctrine.desiredControlledSystems}`,
    credits: Math.round(f.credits),
    population: Math.round(f.populationTotal),
    shipsBuilt: f.shipsBuilt,
    shipsLost: f.shipsLost,
  })),
);

const factionColumnHelper = createColumnHelper<FactionRow>();
const factionColumns = [
  factionColumnHelper.accessor("label", { header: "Faction" }),
  factionColumnHelper.accessor("color", {
    header: "Color",
    cell: (info) => renderColorCell(info.getValue()),
  }),
  factionColumnHelper.accessor("planCycle", { header: "Cycle" }),
  factionColumnHelper.accessor("posture", { header: "Posture" }),
  factionColumnHelper.accessor("fronts", { header: "Fronts" }),
  factionColumnHelper.accessor("leadCampaign", { header: "Lead Campaign" }),
  factionColumnHelper.accessor("leadObjective", { header: "Lead Objective" }),
  factionColumnHelper.accessor("commitments", { header: "Commitments" }),
  factionColumnHelper.accessor("reserves", { header: "Reserves" }),
  factionColumnHelper.accessor("bottleneck", { header: "Bottleneck" }),
  factionColumnHelper.accessor("intent", { header: "Strategic Intent" }),
  factionColumnHelper.accessor("decision", { header: "Recent Decision" }),
  factionColumnHelper.accessor("memory", { header: "Memory" }),
  factionColumnHelper.accessor("economy", { header: "Economy" }),
  factionColumnHelper.accessor("threat", { header: "Threat" }),
  factionColumnHelper.accessor("systems", { header: "Systems" }),
  factionColumnHelper.accessor("credits", { header: "Credits" }),
  factionColumnHelper.accessor("population", { header: "Pop" }),
  factionColumnHelper.accessor("shipsBuilt", { header: "Built" }),
  factionColumnHelper.accessor("shipsLost", { header: "Lost" }),
];

const factionFilter = ref("");
const factionSorting = ref<SortingState>([]);
const factionOrder = useColumnOrder(["label", "color", "planCycle", "posture", "fronts", "leadCampaign", "leadObjective", "commitments", "reserves", "bottleneck", "intent", "decision", "memory", "economy", "threat", "systems", "credits", "population", "shipsBuilt", "shipsLost"]);

const factionTable = useVueTable({
  get data() { return factionRows.value; },
  columns: factionColumns,
  state: {
    get globalFilter() { return factionFilter.value; },
    get sorting() { return factionSorting.value; },
    get columnOrder() { return factionOrder.columnOrder.value; },
  },
  onGlobalFilterChange: (v) => { factionFilter.value = String(v); },
  onSortingChange: (updater) => {
    factionSorting.value = typeof updater === "function" ? updater(factionSorting.value) : updater;
  },
  onColumnOrderChange: factionOrder.onColumnOrderChange,
  getCoreRowModel: getCoreRowModel(),
  getFilteredRowModel: getFilteredRowModel(),
  getSortedRowModel: getSortedRowModel(),
});

// ── Row counts ─────────────────────────────────────────────────────────────

const tabs: { id: TabId; label: string }[] = [
  { id: "player", label: "Player" },
  { id: "geopolitics", label: "Geopolitics" },
  { id: "ships", label: "Ships" },
  { id: "stations", label: "Stations" },
  { id: "factions", label: "Factions" },
];

const activeFilter = computed({
  get: () => {
    if (activeTab.value === "player") return "";
    if (activeTab.value === "geopolitics") return "";
    if (activeTab.value === "ships") return shipFilter.value;
    if (activeTab.value === "stations") return stationFilter.value;
    return factionFilter.value;
  },
  set: (v: string) => {
    if (activeTab.value === "player") return;
    if (activeTab.value === "geopolitics") return;
    if (activeTab.value === "ships") shipFilter.value = v;
    else if (activeTab.value === "stations") stationFilter.value = v;
    else factionFilter.value = v;
  },
});

const activeRowCount = computed(() => {
  if (activeTab.value === "player") {
    return (playerFactionStore.playerFaction?.assetRegistry.shipIds.length ?? 0)
      + (playerFactionStore.playerFaction?.assetRegistry.stationIds.length ?? 0);
  }
  if (activeTab.value === "geopolitics") {
    const geopolitics = gmStore.geopolitics;
    return (geopolitics?.diplomacy.relations.length ?? 0)
      + (geopolitics?.territory.controlStates.length ?? 0)
      + (geopolitics?.economyRegions.regions.length ?? 0);
  }
  if (activeTab.value === "ships") return shipTable.getFilteredRowModel().rows.length;
  if (activeTab.value === "stations") return stationTable.getFilteredRowModel().rows.length;
  return factionTable.getFilteredRowModel().rows.length;
});

const activeTotalCount = computed(() => {
  if (activeTab.value === "player") return activeRowCount.value;
  if (activeTab.value === "geopolitics") return activeRowCount.value;
  if (activeTab.value === "ships") return gmStore.ships.length;
  if (activeTab.value === "stations") return gmStore.stations.length;
  return gmStore.factions.length;
});

// ── Row interaction ────────────────────────────────────────────────────────

function onShipClick(row: ShipRow) {
  selectionStore.selectSelection({ id: row.id, kind: "ship", label: row.label }, "ui");
}

function onShipDblClick(row: ShipRow) {
  selectionStore.selectSelection({ id: row.id, kind: "ship", label: row.label }, "ui");
  emit("focus", row.id, "ship");
}

function onStationClick(row: StationRow) {
  selectionStore.selectSelection({ id: row.id, kind: "station", label: row.label }, "ui");
}

function onStationDblClick(row: StationRow) {
  selectionStore.selectSelection({ id: row.id, kind: "station", label: row.label }, "ui");
  emit("focus", row.id, "station");
}

function isShipSelected(id: string) {
  return selectedEntityKind.value === "ship" && selectedEntityId.value === id;
}

function isStationSelected(id: string) {
  return selectedEntityKind.value === "station" && selectedEntityId.value === id;
}

// ── Orders tooltip ─────────────────────────────────────────────────────────

const ordersTooltip = ref<{
  visible: boolean;
  x: number;
  y: number;
  orders: MarketOrderSnapshot[];
}>({ visible: false, x: 0, y: 0, orders: [] });

function showOrdersTooltip(e: MouseEvent, orders: MarketOrderSnapshot[]) {
  if (orders.length === 0) return;
  ordersTooltip.value = { visible: true, x: e.clientX, y: e.clientY, orders };
}

function moveOrdersTooltip(e: MouseEvent) {
  if (ordersTooltip.value.visible) {
    ordersTooltip.value.x = e.clientX;
    ordersTooltip.value.y = e.clientY;
  }
}

function hideOrdersTooltip() {
  ordersTooltip.value.visible = false;
}
</script>

<template>
  <GmWindow
    title="Empire / AI States"
    :initial-width="980"
    :initial-height="560"
    :initial-x="80"
    :initial-y="80"
    @close="emit('close')"
  >
    <div class="flex h-full flex-col">
      <!-- Tab bar + search -->
      <div class="gm-toolbar flex shrink-0 items-center gap-3 px-3 py-2">
        <div class="flex gap-1">
          <button
            v-for="tab in tabs"
            :key="tab.id"
            type="button"
            class="gm-tab-btn rounded px-3 py-1 text-xs font-semibold uppercase tracking-widest transition"
            :class="activeTab === tab.id ? 'gm-tab-btn--active' : ''"
            @click="activeTab = tab.id"
          >
            {{ tab.label }}
          </button>
        </div>

        <div v-if="activeTab !== 'player' && activeTab !== 'geopolitics'" class="relative flex-1">
          <input
            v-model="activeFilter"
            class="gm-search-input w-full rounded border py-1 pl-7 pr-7 text-xs"
            placeholder="Filter…"
            type="search"
          />
          <svg class="pointer-events-none absolute left-2 top-1/2 -translate-y-1/2 opacity-40" width="12" height="12" viewBox="0 0 16 16" fill="currentColor">
            <path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001q.044.06.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1 1 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0" />
          </svg>
          <button
            v-if="activeFilter"
            type="button"
            class="absolute right-2 top-1/2 -translate-y-1/2 text-[10px] opacity-40 hover:opacity-80"
            @click="activeFilter = ''"
          >
            ✕
          </button>
        </div>
        <div v-else class="flex-1 text-xs opacity-60">
          {{ activeTab === "player" ? "Player empire control, policy, and observability." : "Diplomacy, territory, and regional economy observability." }}
        </div>

        <span class="gm-row-count shrink-0 font-mono text-xs tabular-nums opacity-60">
          {{ activeRowCount }} / {{ activeTotalCount }}
        </span>
      </div>

      <!-- Ships table -->
      <div
        v-show="activeTab === 'player'"
        class="gm-table-container min-h-0 flex-1 overflow-auto"
      >
        <GmPlayerFactionPanel />
      </div>

      <div
        v-show="activeTab === 'geopolitics'"
        class="gm-table-container min-h-0 flex-1 overflow-auto"
      >
        <GmGeopoliticsPanel />
      </div>

      <!-- Ships table -->
      <div
        v-show="activeTab === 'ships'"
        class="gm-table-container min-h-0 flex-1 overflow-auto"
      >
        <table class="gm-table w-full min-w-max border-separate border-spacing-0 text-xs">
          <thead class="sticky top-0 z-10">
            <tr
              v-for="headerGroup in shipTable.getHeaderGroups()"
              :key="headerGroup.id"
            >
              <th
                v-for="header in headerGroup.headers"
                :key="header.id"
                class="gm-th whitespace-nowrap px-3 py-2 text-left"
                :class="[
                  header.column.getCanSort() ? 'cursor-pointer' : '',
                  shipOrder.draggingId.value === header.column.id ? 'gm-th--dragging' : '',
                  shipOrder.overId.value === header.column.id ? 'gm-th--dragover' : '',
                ]"
                draggable="true"
                @dragstart="shipOrder.onDragStart($event, header.column.id)"
                @dragover="shipOrder.onDragOver($event, header.column.id)"
                @dragleave="shipOrder.onDragLeave()"
                @drop="shipOrder.onDrop(header.column.id)"
                @dragend="shipOrder.onDragEnd()"
                @click="header.column.getToggleSortingHandler()?.($event)"
              >
                <span class="flex select-none items-center gap-1">
                  <FlexRender
                    :render="header.column.columnDef.header"
                    :props="header.getContext()"
                  />
                  <span class="text-[10px] opacity-60">
                    {{ header.column.getIsSorted() === "asc" ? "↑" : header.column.getIsSorted() === "desc" ? "↓" : "" }}
                  </span>
                </span>
              </th>
            </tr>
          </thead>
          <tbody>
            <tr
              v-for="row in shipTable.getRowModel().rows"
              :key="row.id"
              class="gm-tr cursor-pointer"
              :class="isShipSelected(row.original.id) ? 'gm-tr--selected' : ''"
              @click="onShipClick(row.original)"
              @dblclick="onShipDblClick(row.original)"
            >
              <td
                v-for="cell in row.getVisibleCells()"
                :key="cell.id"
                class="gm-td whitespace-nowrap px-3 py-1.5"
              >
                <span
                  v-if="cell.column.id === 'class'"
                  class="gm-badge rounded px-1.5 py-0.5 font-mono text-[10px] uppercase tracking-wider"
                >
                  <FlexRender :render="cell.column.columnDef.cell" :props="cell.getContext()" />
                </span>
                <FlexRender
                  v-else
                  :render="cell.column.columnDef.cell"
                  :props="cell.getContext()"
                />
              </td>
            </tr>
            <tr v-if="shipTable.getRowModel().rows.length === 0">
              <td :colspan="shipColumns.length" class="gm-td px-3 py-6 text-center opacity-40">
                No ships match the filter.
              </td>
            </tr>
          </tbody>
        </table>
      </div>

      <!-- Stations table -->
      <div
        v-show="activeTab === 'stations'"
        class="gm-table-container min-h-0 flex-1 overflow-auto"
      >
        <table class="gm-table w-full min-w-max border-separate border-spacing-0 text-xs">
          <thead class="sticky top-0 z-10">
            <tr
              v-for="headerGroup in stationTable.getHeaderGroups()"
              :key="headerGroup.id"
            >
              <th
                v-for="header in headerGroup.headers"
                :key="header.id"
                class="gm-th whitespace-nowrap px-3 py-2 text-left"
                :class="[
                  header.column.getCanSort() ? 'cursor-pointer' : '',
                  stationOrder.draggingId.value === header.column.id ? 'gm-th--dragging' : '',
                  stationOrder.overId.value === header.column.id ? 'gm-th--dragover' : '',
                ]"
                draggable="true"
                @dragstart="stationOrder.onDragStart($event, header.column.id)"
                @dragover="stationOrder.onDragOver($event, header.column.id)"
                @dragleave="stationOrder.onDragLeave()"
                @drop="stationOrder.onDrop(header.column.id)"
                @dragend="stationOrder.onDragEnd()"
                @click="header.column.getToggleSortingHandler()?.($event)"
              >
                <span class="flex select-none items-center gap-1">
                  <FlexRender
                    :render="header.column.columnDef.header"
                    :props="header.getContext()"
                  />
                  <span class="text-[10px] opacity-60">
                    {{ header.column.getIsSorted() === "asc" ? "↑" : header.column.getIsSorted() === "desc" ? "↓" : "" }}
                  </span>
                </span>
              </th>
            </tr>
          </thead>
          <tbody>
            <tr
              v-for="row in stationTable.getRowModel().rows"
              :key="row.id"
              class="gm-tr cursor-pointer"
              :class="isStationSelected(row.original.id) ? 'gm-tr--selected' : ''"
              @click="onStationClick(row.original)"
              @dblclick="onStationDblClick(row.original)"
            >
              <td
                v-for="cell in row.getVisibleCells()"
                :key="cell.id"
                class="gm-td whitespace-nowrap px-3 py-1.5"
              >
                <span
                  v-if="cell.column.id === 'category'"
                  class="gm-badge rounded px-1.5 py-0.5 font-mono text-[10px] uppercase tracking-wider"
                >
                  <FlexRender :render="cell.column.columnDef.cell" :props="cell.getContext()" />
                </span>
                <span
                  v-else-if="cell.column.id === 'orders'"
                  :class="row.original.orderDetails.length > 0 ? 'gm-orders-trigger' : ''"
                  @mouseenter="showOrdersTooltip($event, row.original.orderDetails)"
                  @mousemove="moveOrdersTooltip"
                  @mouseleave="hideOrdersTooltip"
                >
                  <FlexRender :render="cell.column.columnDef.cell" :props="cell.getContext()" />
                </span>
                <FlexRender
                  v-else
                  :render="cell.column.columnDef.cell"
                  :props="cell.getContext()"
                />
              </td>
            </tr>
            <tr v-if="stationTable.getRowModel().rows.length === 0">
              <td :colspan="stationColumns.length" class="gm-td px-3 py-6 text-center opacity-40">
                No stations match the filter.
              </td>
            </tr>
          </tbody>
        </table>
      </div>

      <!-- Factions table -->
      <div
        v-show="activeTab === 'factions'"
        class="gm-table-container min-h-0 flex-1 overflow-auto"
      >
        <table class="gm-table w-full min-w-max border-separate border-spacing-0 text-xs">
          <thead class="sticky top-0 z-10">
            <tr
              v-for="headerGroup in factionTable.getHeaderGroups()"
              :key="headerGroup.id"
            >
              <th
                v-for="header in headerGroup.headers"
                :key="header.id"
                class="gm-th whitespace-nowrap px-3 py-2 text-left"
                :class="[
                  header.column.getCanSort() ? 'cursor-pointer' : '',
                  factionOrder.draggingId.value === header.column.id ? 'gm-th--dragging' : '',
                  factionOrder.overId.value === header.column.id ? 'gm-th--dragover' : '',
                ]"
                draggable="true"
                @dragstart="factionOrder.onDragStart($event, header.column.id)"
                @dragover="factionOrder.onDragOver($event, header.column.id)"
                @dragleave="factionOrder.onDragLeave()"
                @drop="factionOrder.onDrop(header.column.id)"
                @dragend="factionOrder.onDragEnd()"
                @click="header.column.getToggleSortingHandler()?.($event)"
              >
                <span class="flex select-none items-center gap-1">
                  <FlexRender
                    :render="header.column.columnDef.header"
                    :props="header.getContext()"
                  />
                  <span class="text-[10px] opacity-60">
                    {{ header.column.getIsSorted() === "asc" ? "↑" : header.column.getIsSorted() === "desc" ? "↓" : "" }}
                  </span>
                </span>
              </th>
            </tr>
          </thead>
          <tbody>
            <tr
              v-for="row in factionTable.getRowModel().rows"
              :key="row.id"
              class="gm-tr"
            >
              <td
                v-for="cell in row.getVisibleCells()"
                :key="cell.id"
                class="gm-td whitespace-nowrap px-3 py-1.5"
              >
                <FlexRender :render="cell.column.columnDef.cell" :props="cell.getContext()" />
              </td>
            </tr>
            <tr v-if="factionTable.getRowModel().rows.length === 0">
              <td :colspan="factionColumns.length" class="gm-td px-3 py-6 text-center opacity-40">
                No factions match the filter.
              </td>
            </tr>
          </tbody>
        </table>
      </div>
    </div>
  </GmWindow>

  <Teleport to="body">
    <div
      v-if="ordersTooltip.visible && ordersTooltip.orders.length > 0"
      class="gm-orders-tooltip"
      :style="{ left: `${ordersTooltip.x + 14}px`, top: `${ordersTooltip.y + 14}px` }"
    >
      <table class="gm-orders-tooltip-table">
        <thead>
          <tr>
            <th>Item</th>
            <th>Kind</th>
            <th>Amount</th>
            <th>Remaining</th>
            <th>Valuation</th>
            <th>State</th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="order in ordersTooltip.orders" :key="order.id">
            <td>{{ order.itemId }}</td>
            <td>{{ titleCaseToken(order.kind) }}</td>
            <td class="tabular-nums">{{ order.amount }}</td>
            <td class="tabular-nums">{{ order.remainingAmount }}</td>
            <td class="tabular-nums">{{ order.valuation }}</td>
            <td>{{ titleCaseToken(order.state) }}</td>
          </tr>
        </tbody>
      </table>
    </div>
  </Teleport>
</template>
