"use client";

import React, { useCallback, useEffect, useMemo, useState } from "react";
import Link from "next/link";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import {
  Bar,
  CartesianGrid,
  ComposedChart,
  Line,
  ReferenceLine,
  ResponsiveContainer,
  Tooltip,
  XAxis,
  YAxis,
} from "recharts";

/**
 * API SHAPES (from your route.ts)
 */
type ApiParetoRow = {
  reasonCode: string;
  reasonLabel: string;
  minutesLost?: number;
  scrapQty?: number;
  pctOfTotal: number; // percent 0..100
  cumulativePct: number; // percent 0..100
  count: number;
};

type ApiParetoRes = {
  ok: boolean;
  error?: string;
  orgId?: string;
  machineId?: string | null;
  kind?: "downtime" | "scrap";
  range?: "24h" | "7d" | "30d";
  start?: string;
  totalMinutesLost?: number;
  totalScrap?: number;
  rows?: ApiParetoRow[];
  top3?: ApiParetoRow[];
  threshold80?: { index: number; reasonCode: string; reasonLabel: string } | null;
  total?: number;
};

type LegacyParetoItem = {
  reasonCode?: string;
  reasonLabel?: string;
  value?: number; // minutes (downtime) or qty (scrap)
  count?: number;
  cumPct?: number;
};

type ApiDowntimeEvent = {
  id: string;
  episodeId: string | null;
  machineId: string;
  machineName: string | null;

  reasonCode: string;
  reasonLabel: string;
  reasonText: string | null;

  durationSeconds: number | null;
  durationMinutes: number | null;

  startAt: string | null;
  endAt: string | null;
  capturedAt: string | null;

  workOrderId: string | null;
  meta: any | null;
  createdAt: string | null;
};

type ApiDowntimeEventsRes = {
  ok: boolean;
  error?: string;
  orgId?: string;
  range?: "24h" | "7d" | "30d";
  start?: string;
  machineId?: string | null;
  reasonCode?: string | null;
  limit?: number;
  before?: string | null;
  nextBefore?: string | null;
  events?: ApiDowntimeEvent[];
};

type ApiReasonCatalogRow = {
  kind: "downtime" | "scrap";
  categoryId: string;
  categoryLabel: string;
  detailId: string;
  detailLabel: string;
  reasonCode: string;
  reasonLabel: string;
};

type ApiReasonCatalogRes = {
  ok: boolean;
  error?: string;
  kind?: "downtime" | "scrap";
  catalogVersion?: number;
  rows?: ApiReasonCatalogRow[];
};

function fmtDT(iso: string | null) {
  if (!iso) return "—";
  const d = new Date(iso);
  return d.toLocaleString("en-US", { hour12: true });
}

function normalizeParetoRes(input: ApiParetoRes): ApiParetoRes {
  const rows = Array.isArray(input?.rows) ? input.rows : [];
  if (rows.length > 0) return input;

  // Support a legacy envelope where the server returns `items[]` instead of `rows[]`.
  const legacyItems = (input as any)?.items as unknown;
  if (!Array.isArray(legacyItems) || legacyItems.length === 0) return input;

  const items = legacyItems as LegacyParetoItem[];
  const safeItems = items
    .map((it) => ({
      reasonCode: String(it?.reasonCode ?? "").trim(),
      reasonLabel: String(it?.reasonLabel ?? it?.reasonCode ?? "").trim(),
      value: typeof it?.value === "number" && Number.isFinite(it.value) ? it.value : 0,
      count: typeof it?.count === "number" && Number.isFinite(it.count) ? it.count : 0,
    }))
    .filter((x) => x.reasonCode);

  // Legacy `items` are usually pre-sorted by value desc; enforce it anyway.
  safeItems.sort((a, b) => b.value - a.value);

  const total = safeItems.reduce((acc, x) => acc + x.value, 0);
  let cum = 0;
  let threshold80Index: number | null = null;

  const outRows: ApiParetoRow[] = safeItems.map((x, idx) => {
    const pctOfTotal = total > 0 ? (x.value / total) * 100 : 0;
    cum += x.value;
    const cumulativePct = total > 0 ? (cum / total) * 100 : 0;
    if (threshold80Index === null && cumulativePct >= 80) threshold80Index = idx;

    return {
      reasonCode: x.reasonCode,
      reasonLabel: x.reasonLabel || x.reasonCode,
      minutesLost: input.kind === "scrap" ? undefined : x.value,
      scrapQty: input.kind === "scrap" ? x.value : undefined,
      pctOfTotal,
      cumulativePct,
      count: x.count,
    };
  });

  const threshold80 =
    threshold80Index === null
      ? null
      : {
          index: threshold80Index,
          reasonCode: outRows[threshold80Index].reasonCode,
          reasonLabel: outRows[threshold80Index].reasonLabel,
        };

  return {
    ...input,
    rows: outRows,
    top3: outRows.slice(0, 3),
    threshold80,
    totalMinutesLost: input.kind === "scrap" ? undefined : total,
    totalScrap: input.kind === "scrap" ? total : undefined,
    total,
  };
}

function buildParetoFromEvents(events: ApiDowntimeEvent[]): ApiParetoRes | null {
  if (!Array.isArray(events) || events.length === 0) return null;

  const byCode = new Map<
    string,
    { reasonCode: string; reasonLabel: string; minutes: number; count: number }
  >();

  for (const e of events) {
    const reasonCode = String(e?.reasonCode ?? "").trim();
    if (!reasonCode) continue;
    const reasonLabel = String(e?.reasonLabel ?? reasonCode).trim() || reasonCode;
    const minutes =
      (typeof e?.durationMinutes === "number" && Number.isFinite(e.durationMinutes)
        ? e.durationMinutes
        : null) ??
      (typeof e?.durationSeconds === "number" && Number.isFinite(e.durationSeconds)
        ? e.durationSeconds / 60
        : 0);

    const slot =
      byCode.get(reasonCode) ?? { reasonCode, reasonLabel, minutes: 0, count: 0 };
    slot.minutes += Math.max(0, minutes);
    slot.count += 1;
    // prefer the most recent non-empty label if they differ
    if (reasonLabel && reasonLabel !== reasonCode) slot.reasonLabel = reasonLabel;
    byCode.set(reasonCode, slot);
  }

  const items = [...byCode.values()].filter((x) => x.minutes > 0 || x.count > 0);
  items.sort((a, b) => b.minutes - a.minutes);

  const totalMinutesLost = items.reduce((acc, x) => acc + x.minutes, 0);
  let cum = 0;
  let threshold80Index: number | null = null;

  const rows: ApiParetoRow[] = items.map((x, idx) => {
    const pctOfTotal = totalMinutesLost > 0 ? (x.minutes / totalMinutesLost) * 100 : 0;
    cum += x.minutes;
    const cumulativePct = totalMinutesLost > 0 ? (cum / totalMinutesLost) * 100 : 0;
    if (threshold80Index === null && cumulativePct >= 80) threshold80Index = idx;
    return {
      reasonCode: x.reasonCode,
      reasonLabel: x.reasonLabel,
      minutesLost: Math.round(x.minutes * 10) / 10,
      pctOfTotal,
      cumulativePct,
      count: x.count,
    };
  });

  const threshold80 =
    threshold80Index === null
      ? null
      : {
          index: threshold80Index,
          reasonCode: rows[threshold80Index].reasonCode,
          reasonLabel: rows[threshold80Index].reasonLabel,
        };

  return {
    ok: true,
    kind: "downtime",
    totalMinutesLost: Math.round(totalMinutesLost * 10) / 10,
    rows,
    top3: rows.slice(0, 3),
    threshold80,
    total: totalMinutesLost,
  };
}


type Range = "24h" | "7d" | "30d";
type Metric = "minutes" | "count";

type MetricRow = {
  reasonCode: string;
  reasonLabel: string;
  value: number; // minutes OR count
  count: number; // always count (stops)
  pctOfTotal: number; // percent 0..100 in selected metric
  cumulativePct: number; // percent 0..100 in selected metric
  minutesLost?: number; // if available
};

function fmtNum(n: number, digits = 0) {
  return new Intl.NumberFormat("en-US", {
    maximumFractionDigits: digits,
    minimumFractionDigits: digits,
  }).format(n);
}

function fmtPct(pct: number, digits = 0) {
  return `${fmtNum(pct, digits)}%`;
}

function fmtHoursFromMinutes(min: number) {
  const hrs = min / 60;
  return hrs >= 10 ? `${fmtNum(hrs, 0)} hrs` : `${fmtNum(hrs, 1)} hrs`;
}

function cn(...xs: Array<string | false | null | undefined>) {
  return xs.filter(Boolean).join(" ");
}

function buildSearch(params: URLSearchParams, patch: Record<string, string | null>) {
  const next = new URLSearchParams(params.toString());
  Object.entries(patch).forEach(([k, v]) => {
    if (v === null) next.delete(k);
    else next.set(k, v);
  });
  return next.toString();
}

/**
 * Derive a Pareto set for Minutes or Count from the same API response.
 * - Your API always returns rows sorted by VALUE (minutes for downtime, scrapQty for scrap).
 * - For Metric=COUNT, we re-sort by count and recompute pct/cum on client.
 */
function computeMetricRows(base: ApiParetoRow[], metric: Metric): MetricRow[] {
  const safe = base ?? [];

  if (metric === "minutes") {
    const rows: MetricRow[] = safe.map((r) => ({
      reasonCode: r.reasonCode,
      reasonLabel: r.reasonLabel,
      value: r.minutesLost ?? 0,
      count: r.count ?? 0,
      pctOfTotal: r.pctOfTotal ?? 0,
      cumulativePct: r.cumulativePct ?? 0,
      minutesLost: r.minutesLost ?? 0,
    }));
    return rows;
  }

  // metric === "count"
  const sorted = [...safe].sort((a, b) => (b.count ?? 0) - (a.count ?? 0));
  const total = sorted.reduce((acc, r) => acc + (r.count ?? 0), 0);

  let cum = 0;
  const out: MetricRow[] = sorted.map((r) => {
    const v = r.count ?? 0;
    const pct = total > 0 ? (v / total) * 100 : 0;
    cum += v;
    const cumPct = total > 0 ? (cum / total) * 100 : 0;

    return {
      reasonCode: r.reasonCode,
      reasonLabel: r.reasonLabel,
      value: v,
      count: v,
      pctOfTotal: pct,
      cumulativePct: cumPct,
      minutesLost: r.minutesLost ?? 0,
    };
  });

  return out;
}

function findUnclassifiedPct(rows: MetricRow[]) {
  const hit = rows.find((r) => {
    const code = (r.reasonCode ?? "").toLowerCase();
    const label = (r.reasonLabel ?? "").toLowerCase();
    return code.includes("unclass") || code.includes("unknown") || label.includes("unclass") || label.includes("unknown");
  });
  return hit ? hit.pctOfTotal : 0;
}

/**
 * Right-side drawer (investigation)
 * Built in the same style as MachineDetailClient’s Modal overlay.
 */
function ReasonDrawer({
  open,
  onClose,
  row,
  metric,
}: {
  open: boolean;
  onClose: () => void;
  row: MetricRow | null;
  metric: Metric;
}) {
  if (!open || !row) return null;

  const avgMin =
    row.count > 0 && row.minutesLost != null ? row.minutesLost / row.count : null;

  return (
    <div className="fixed inset-0 z-50">
      <div className="absolute inset-0 bg-black/70" onClick={onClose} />
      <div className="absolute right-0 top-0 h-full w-full max-w-[520px] overflow-hidden border-l border-white/10 bg-zinc-950/70 backdrop-blur-xl">
        <div
          className="pointer-events-none absolute inset-0 opacity-70"
          style={{
            background:
              "radial-gradient(900px 500px at 20% 10%, rgba(16,185,129,.18), transparent 60%)," +
              "radial-gradient(900px 500px at 85% 30%, rgba(59,130,246,.12), transparent 60%)," +
              "radial-gradient(900px 600px at 50% 100%, rgba(244,63,94,.10), transparent 60%)",
          }}
        />
        <div className="relative flex h-full flex-col">
          <div className="flex items-start justify-between gap-3 border-b border-white/10 p-5">
            <div className="min-w-0">
              <div className="text-sm font-semibold text-white">Reason detail</div>
              <div className="mt-1 truncate text-xs text-zinc-400">{row.reasonLabel}</div>
            </div>
            <button
              onClick={onClose}
              className="rounded-xl border border-white/10 bg-white/5 px-3 py-1.5 text-xs text-white hover:bg-white/10"
            >
              Close
            </button>
          </div>

          <div className="flex-1 overflow-y-auto p-5 no-scrollbar">
            <div className="grid grid-cols-2 gap-3">
              <div className="rounded-2xl border border-white/10 bg-white/5 p-4">
                <div className="text-xs text-zinc-400">
                  {metric === "minutes" ? "Downtime" : "Stops"}
                </div>
                <div className="mt-2 text-2xl font-semibold text-white">
                  {metric === "minutes" ? `${fmtNum(row.value, 1)} min` : fmtNum(row.value, 0)}
                </div>
                <div className="mt-1 text-xs text-zinc-400">{fmtPct(row.pctOfTotal, 1)} share</div>
              </div>

              <div className="rounded-2xl border border-white/10 bg-white/5 p-4">
                <div className="text-xs text-zinc-400">Stops</div>
                <div className="mt-2 text-2xl font-semibold text-white">{fmtNum(row.count, 0)}</div>
                <div className="mt-1 text-xs text-zinc-400">
                  {avgMin == null ? "Avg duration —" : `Avg ${fmtNum(avgMin, 1)} min`}
                </div>
              </div>
            </div>

            <div className="mt-4 rounded-2xl border border-white/10 bg-white/5 p-4">
              <div className="text-sm font-semibold text-white">Investigation (next)</div>
              <div className="mt-1 text-xs text-zinc-400">
                Hook the following panels once you add endpoints for events + breakdowns:
              </div>
              <ul className="mt-3 list-disc space-y-1 pl-5 text-xs text-zinc-300">
                <li>Last 10 events (timestamp, duration, operator note)</li>
                <li>Breakdown by machine / shift / work order</li>
                <li>Duration histogram (micro vs macro)</li>
                <li>Create action (owner, due date, status)</li>
              </ul>
            </div>

            <div className="mt-4 rounded-2xl border border-white/10 bg-black/25 p-4 text-xs text-zinc-400">
              Tip: keep this drawer “fast”. The table + drawer combo is what makes the page feel like a tool.
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

function KPI({
  label,
  value,
  sub,
  accent,
}: {
  label: string;
  value: string;
  sub?: string;
  accent?: "emerald" | "yellow" | "rose" | "zinc";
}) {
  const ring =
    accent === "emerald"
      ? "border-emerald-500/20"
      : accent === "yellow"
      ? "border-yellow-500/20"
      : accent === "rose"
      ? "border-rose-500/20"
      : "border-white/10";

  return (
    <div className={cn("rounded-2xl border bg-white/5 p-5", ring)}>
      <div className="text-xs text-zinc-400">{label}</div>
      <div className="mt-2 text-3xl font-semibold text-white">{value}</div>
      {sub ? <div className="mt-2 text-xs text-zinc-400">{sub}</div> : null}
    </div>
  );
}
const DAY_LABELS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];

function nextHourBoundary(d: Date) {
  const x = new Date(d);
  x.setMinutes(0, 0, 0);
  x.setHours(x.getHours() + 1);
  return x;
}

function getEventInterval(e: ApiDowntimeEvent): { start: Date | null; end: Date | null } {
  const startIso = e.startAt ?? e.capturedAt;
  if (!startIso) return { start: null, end: null };

  const start = new Date(startIso);
  if (Number.isNaN(start.getTime())) return { start: null, end: null };

  // Prefer endAt if present
  if (e.endAt) {
    const end = new Date(e.endAt);
    if (!Number.isNaN(end.getTime()) && end > start) return { start, end };
  }

  // Fall back to duration fields
  const durMin =
    e.durationMinutes ??
    (e.durationSeconds != null ? e.durationSeconds / 60 : null);

  if (durMin != null && durMin > 0) {
    const end = new Date(start.getTime() + durMin * 60_000);
    return { start, end };
  }

  return { start, end: null };
}

/**
 * Build heatmap matrix [7 days][24 hours]
 * - metric="minutes": distributes duration across hour buckets (accurate)
 * - metric="count": increments the start hour bucket
 */
function buildHeatmapMatrix(events: ApiDowntimeEvent[], metric: Metric) {
  const m = Array.from({ length: 7 }, () => Array.from({ length: 24 }, () => 0));

  for (const e of events) {
    const { start, end } = getEventInterval(e);
    if (!start) continue;

    if (metric === "count") {
      m[start.getDay()][start.getHours()] += 1;
      continue;
    }

    if (!end) continue;

    let t = start;
    while (t < end) {
      const day = t.getDay();
      const hour = t.getHours();

      const boundary = nextHourBoundary(t);
      const segEnd = boundary < end ? boundary : end;
      const segMin = (segEnd.getTime() - t.getTime()) / 60_000;

      m[day][hour] += segMin;
      t = segEnd;
    }
  }

  let max = 0;
  for (let d = 0; d < 7; d++) {
    for (let h = 0; h < 24; h++) max = Math.max(max, m[d][h]);
  }

  return { matrix: m, max };
}

function eventTouchesSlot(e: ApiDowntimeEvent, slotDay: number, slotHour: number) {
  const { start, end } = getEventInterval(e);
  if (!start) return false;

  // Count metric: consider start bucket
  if (!end) return start.getDay() === slotDay && start.getHours() === slotHour;

  // Minutes metric: any overlap with that (day, hour) bucket
  let t = start;
  while (t < end) {
    if (t.getDay() === slotDay && t.getHours() === slotHour) return true;
    const boundary = nextHourBoundary(t);
    t = boundary < end ? boundary : end;
  }
  return false;
}

function heatColor(v: number, metric: Metric) {
  // "Good" = green even when v=0
  if (v <= 0) return { bg: "rgba(34,197,94,0.18)", label: "Good" };

  if (metric === "minutes") {
    // per-hour downtime minutes severity
    if (v < 2)  return { bg: "rgba(34,197,94,0.45)", label: "Low" };
    if (v < 6)  return { bg: "rgba(234,179,8,0.55)", label: "Watch" };   // yellow
    if (v < 15) return { bg: "rgba(249,115,22,0.65)", label: "High" };    // orange
    return        { bg: "rgba(239,68,68,0.75)", label: "Critical" };      // red
  }

  // metric === "count"
  if (v <= 1) return { bg: "rgba(34,197,94,0.45)", label: "Low" };
  if (v <= 3) return { bg: "rgba(234,179,8,0.55)", label: "Watch" };
  if (v <= 6) return { bg: "rgba(249,115,22,0.65)", label: "High" };
  return        { bg: "rgba(239,68,68,0.75)", label: "Critical" };
}

function Heatmap({
  events,
  metric,
  selected,
  onSelect,
  onClear,
}: {
  events: ApiDowntimeEvent[];
  metric: Metric;
  selected: { day: number; hour: number } | null;
  onSelect: (day: number, hour: number) => void;
  onClear: () => void;
}) {
  const { matrix, max } = useMemo(() => buildHeatmapMatrix(events, metric), [events, metric]);

  const hourLabels = Array.from({ length: 24 }, (_, h) =>
    h % 2 === 0 ? String(h).padStart(2, "0") : ""
  );

  const hasData = max > 0;

  return (
    <div className="mt-4 overflow-x-auto rounded-2xl border border-white/10 bg-black/20 p-3">
      <div className="min-w-[860px]">
        <div className="flex items-center justify-between pb-3">
          <div className="text-[11px] text-zinc-500">
            Click a cell to filter Event list by day/hour
          </div>
          {selected ? (
            <button
              onClick={onClear}
              className="rounded-lg border border-white/10 bg-white/5 px-2 py-1 text-[11px] text-zinc-200 hover:bg-white/10"
            >
              Clear heatmap filter
            </button>
          ) : null}
        </div>

        {/* Header row */}
        <div className="grid" style={{ gridTemplateColumns: "56px repeat(24, 28px)" }}>
          <div />
          {hourLabels.map((t, h) => (
            <div key={h} className="pb-2 text-center text-[10px] text-zinc-500">
              {t}
            </div>
          ))}
        </div>

        {/* Rows */}
        {matrix.map((row, dayIdx) => (
          <div
            key={dayIdx}
            className="grid items-center"
            style={{ gridTemplateColumns: "56px repeat(24, 28px)" }}
          >
            <div className="pr-2 text-right text-[11px] text-zinc-500">
              {DAY_LABELS[dayIdx]}
            </div>

            {row.map((v, hour) => {
              const c = heatColor(v, metric);
              const isSelected = selected?.day === dayIdx && selected?.hour === hour;

              const title = `${DAY_LABELS[dayIdx]} ${String(hour).padStart(2, "0")}:00–${String(
                (hour + 1) % 24
              ).padStart(2, "0")}:00\n${
                metric === "minutes" ? `${fmtNum(v, 1)} min` : `${fmtNum(v, 0)} stops`
              }\n${c.label}`;

              return (
                <button
                  key={hour}
                  title={title}
                  onClick={() => onSelect(dayIdx, hour)}
                  className={cn(
                    "h-[22px] w-[22px] rounded-md border border-white/5 transition",
                    "hover:brightness-110",
                    isSelected && "ring-2 ring-emerald-400/60"
                  )}
                  style={{
                    background: c.bg,
                    boxShadow: isSelected ? "0 0 0 1px rgba(16,185,129,0.35)" : undefined,
                  }}
                />
              );
            })}
          </div>
        ))}

        {/* Legend */}
        <div className="mt-3 flex flex-wrap items-center gap-3 text-[11px] text-zinc-500">
          <div className="inline-flex items-center gap-2">
            <span className="h-3 w-3 rounded-sm" style={{ background: "rgba(34,197,94,0.18)" }} />
            Good
          </div>
          <div className="inline-flex items-center gap-2">
            <span className="h-3 w-3 rounded-sm" style={{ background: "rgba(234,179,8,0.55)" }} />
            Watch
          </div>
          <div className="inline-flex items-center gap-2">
            <span className="h-3 w-3 rounded-sm" style={{ background: "rgba(249,115,22,0.65)" }} />
            High
          </div>
          <div className="inline-flex items-center gap-2">
            <span className="h-3 w-3 rounded-sm" style={{ background: "rgba(239,68,68,0.75)" }} />
            Critical
          </div>

          <div className="ml-auto">
            {events.length === 0
              ? "No events loaded for this scope"
              : hasData
              ? `Max cell: ${metric === "minutes" ? `${fmtNum(max, 1)} min` : `${fmtNum(max, 0)} stops`}`
              : "Events loaded, but no usable durations/endAt yet"}
          </div>
        </div>
      </div>
    </div>
  );
}

type ActionStatus = "open" | "in_progress" | "blocked" | "done";
type ActionPriority = "low" | "medium" | "high";

type HeatmapSel = { day: number; hour: number };

type ActionItem = {
  id: string;
  createdAt: string;
  updatedAt: string;

  machineId: string | null;
  reasonCode: string | null;
  hmDay: number | null;
  hmHour: number | null;

  title: string;
  notes: string;
  ownerUserId: string | null;
  ownerName: string | null;
  ownerEmail: string | null;
  dueDate: string | null; // YYYY-MM-DD
  status: ActionStatus;
  priority: ActionPriority;
};

type MemberOption = {
  id: string;
  name?: string | null;
  email: string;
  role: string;
  isActive: boolean;
};

function statusPill(status: ActionStatus) {
  switch (status) {
    case "done":
      return "border-emerald-500/25 bg-emerald-500/10 text-emerald-200";
    case "blocked":
      return "border-rose-500/25 bg-rose-500/10 text-rose-200";
    case "in_progress":
      return "border-sky-500/25 bg-sky-500/10 text-sky-200";
    default:
      return "border-amber-500/25 bg-amber-500/10 text-amber-200";
  }
}

function priorityPill(p: ActionPriority) {
  switch (p) {
    case "high":
      return "border-rose-500/25 bg-rose-500/10 text-rose-200";
    case "medium":
      return "border-yellow-500/25 bg-yellow-500/10 text-yellow-200";
    default:
      return "border-white/10 bg-white/5 text-zinc-200";
  }
}

function isValidNum(x: any) {
  const n = Number(x);
  return Number.isFinite(n);
}

function ActionModal({
  open,
  onClose,
  initial,
  onSave,
  onDelete,
  members,
  isNew,
}: {
  open: boolean;
  onClose: () => void;
  initial: ActionItem;
  onSave: (a: ActionItem, isNew: boolean) => Promise<{ ok: boolean; error?: string }>;
  onDelete?: (id: string) => Promise<{ ok: boolean; error?: string }>;
  members: MemberOption[];
  isNew: boolean;
}) {
  const [draft, setDraft] = React.useState<ActionItem>(initial);
  const [saving, setSaving] = React.useState(false);
  const [saveError, setSaveError] = React.useState<string | null>(null);
  const availableMembers = React.useMemo(() => members, [members]);

  React.useEffect(() => {
    setDraft(initial);
    setSaveError(null);
  }, [initial]);

  if (!open) return null;

  return (
    <div className="fixed inset-0 z-50">
      <div className="absolute inset-0 bg-black/70" onClick={onClose} />
      <div className="absolute left-1/2 top-1/2 w-[92vw] max-w-[560px] -translate-x-1/2 -translate-y-1/2 overflow-hidden rounded-3xl border border-white/10 bg-zinc-950/80 backdrop-blur-xl">
        <div className="flex items-start justify-between gap-3 border-b border-white/10 p-5">
          <div className="min-w-0">
            <div className="text-sm font-semibold text-white">Action</div>
            <div className="mt-1 text-xs text-zinc-400">
              Assign ownership + due date. Keep it short and clear.
            </div>
          </div>
          <button
            onClick={onClose}
            className="rounded-xl border border-white/10 bg-white/5 px-3 py-1.5 text-xs text-white hover:bg-white/10"
          >
            Close
          </button>
        </div>

        <div className="p-5 space-y-4">
          <div>
            <div className="text-[11px] text-zinc-500">Title</div>
            <input
              value={draft.title}
              onChange={(e) => setDraft((d) => ({ ...d, title: e.target.value }))}
              placeholder="e.g. Add checklist for material feed before start-up"
              className="mt-1 h-10 w-full rounded-xl border border-white/10 bg-black/20 px-3 text-sm text-white outline-none placeholder:text-zinc-500"
            />
          </div>

          <div className="grid grid-cols-2 gap-3">
            <div>
              <div className="text-[11px] text-zinc-500">Owner</div>
              <select
                value={draft.ownerUserId ?? ""}
                onChange={(e) =>
                  setDraft((d) => ({
                    ...d,
                    ownerUserId: e.target.value ? e.target.value : null,
                  }))
                }
                className="mt-1 h-10 w-full rounded-xl border border-white/10 bg-black/20 px-3 text-sm text-white outline-none"
              >
                <option value="">Unassigned</option>
                {availableMembers.map((member) => {
                  const label = member.name ? `${member.name} (${member.email})` : member.email;
                  const suffix = member.isActive ? "" : " (inactive)";
                  return (
                    <option key={member.id} value={member.id}>
                      {label}{suffix}
                    </option>
                  );
                })}
              </select>
            </div>

            <div>
              <div className="text-[11px] text-zinc-500">Due date</div>
              <input
                type="date"
                value={draft.dueDate ?? ""}
                onChange={(e) => setDraft((d) => ({ ...d, dueDate: e.target.value || null }))}
                className="mt-1 h-10 w-full rounded-xl border border-white/10 bg-black/20 px-3 text-sm text-white outline-none"
              />
            </div>
          </div>

          <div className="grid grid-cols-2 gap-3">
            <div>
              <div className="text-[11px] text-zinc-500">Status</div>
              <select
                value={draft.status}
                onChange={(e) => setDraft((d) => ({ ...d, status: e.target.value as ActionStatus }))}
                className="mt-1 h-10 w-full rounded-xl border border-white/10 bg-black/20 px-3 text-sm text-white outline-none"
              >
                <option value="open">Open</option>
                <option value="in_progress">In progress</option>
                <option value="blocked">Blocked</option>
                <option value="done">Done</option>
              </select>
            </div>

            <div>
              <div className="text-[11px] text-zinc-500">Priority</div>
              <select
                value={draft.priority}
                onChange={(e) => setDraft((d) => ({ ...d, priority: e.target.value as ActionPriority }))}
                className="mt-1 h-10 w-full rounded-xl border border-white/10 bg-black/20 px-3 text-sm text-white outline-none"
              >
                <option value="low">Low</option>
                <option value="medium">Medium</option>
                <option value="high">High</option>
              </select>
            </div>
          </div>

          <div>
            <div className="text-[11px] text-zinc-500">Notes</div>
            <textarea
              value={draft.notes}
              onChange={(e) => setDraft((d) => ({ ...d, notes: e.target.value }))}
              placeholder="Context, hypothesis, what to verify, etc."
              className="mt-1 h-24 w-full resize-none rounded-xl border border-white/10 bg-black/20 px-3 py-2 text-sm text-white outline-none placeholder:text-zinc-500"
            />
          </div>

          {saveError ? (
            <div className="rounded-xl border border-rose-500/30 bg-rose-500/10 px-3 py-2 text-xs text-rose-200">
              {saveError}
            </div>
          ) : null}

          <div className="flex flex-wrap items-center gap-2 text-[11px] text-zinc-400">
            {draft.machineId ? <span className="rounded-full border border-white/10 bg-white/5 px-2 py-1">Machine</span> : null}
            {draft.reasonCode ? <span className="rounded-full border border-white/10 bg-white/5 px-2 py-1">Reason</span> : null}
            {draft.hmDay != null && draft.hmHour != null ? (
              <span className="rounded-full border border-white/10 bg-white/5 px-2 py-1">Heatmap bucket</span>
            ) : null}
          </div>

          <div className="flex items-center justify-between pt-2">
            <div>
              {onDelete && !isNew ? (
                <button
                  onClick={async () => {
                    if (!draft.id) return;
                    setSaving(true);
                    setSaveError(null);
                    const result = await onDelete(draft.id);
                    if (!result.ok) {
                      setSaveError(result.error || "Failed to delete action");
                      setSaving(false);
                      return;
                    }
                    setSaving(false);
                    onClose();
                  }}
                  disabled={saving}
                  className="rounded-xl border border-rose-500/25 bg-rose-500/10 px-4 py-2 text-sm text-rose-200 hover:bg-rose-500/15 disabled:cursor-not-allowed disabled:opacity-60"
                >
                  Delete
                </button>
              ) : null}
            </div>

            <button
              onClick={async () => {
                setSaving(true);
                setSaveError(null);
                const now = new Date().toISOString();
                const next: ActionItem = { ...draft, updatedAt: now };
                const result = await onSave(next, isNew);
                if (!result.ok) {
                  setSaveError(result.error || "Failed to save action");
                  setSaving(false);
                  return;
                }
                setSaving(false);
                onClose();
              }}
              disabled={saving}
              className="rounded-xl border border-emerald-500/30 bg-emerald-500/10 px-4 py-2 text-sm text-emerald-100 hover:bg-emerald-500/20 disabled:cursor-not-allowed disabled:opacity-60"
            >
              {saving ? "Saving..." : "Save action"}
            </button>
          </div>
        </div>
      </div>
    </div>
  );
}

function ActionsOwnershipPanel({
  machineId,
  reasonCode,
  heatmapSel,
  onFocusReason,
}: {
  machineId: string | null;
  reasonCode: string | null;
  heatmapSel: HeatmapSel | null;
  onFocusReason: (code: string) => void;
}) {
  const [items, setItems] = React.useState<ActionItem[]>([]);
  const [loading, setLoading] = React.useState(true);
  const [error, setError] = React.useState<string | null>(null);
  const [members, setMembers] = React.useState<MemberOption[]>([]);

  const hmDay = heatmapSel?.day ?? null;
  const hmHour = heatmapSel?.hour ?? null;

  const loadActions = useCallback(async () => {
    setLoading(true);
    setError(null);
    try {
      const params = new URLSearchParams();
      if (machineId) params.set("machineId", machineId);
      if (reasonCode) params.set("reasonCode", reasonCode);
      if (hmDay != null && hmHour != null) {
        params.set("hmDay", String(hmDay));
        params.set("hmHour", String(hmHour));
      }
      const qs = params.toString();
      const res = await fetch(`/api/downtime/actions${qs ? `?${qs}` : ""}`, { cache: "no-store" });
      const data = (await res.json().catch(() => ({}))) as {
        ok?: boolean;
        error?: string;
        actions?: ActionItem[];
      };
      if (!res.ok || !data.ok) {
        throw new Error(data.error || "Failed to load actions");
      }
      setItems(Array.isArray(data.actions) ? data.actions : []);
    } catch (err) {
      setError(err instanceof Error ? err.message : "Failed to load actions");
      setItems([]);
    } finally {
      setLoading(false);
    }
  }, [machineId, reasonCode, hmDay, hmHour]);

  useEffect(() => {
    loadActions();
  }, [loadActions]);

  useEffect(() => {
    let alive = true;
    async function loadMembers() {
      try {
        const res = await fetch("/api/org/members", { cache: "no-store" });
        const data = (await res.json().catch(() => ({}))) as {
          ok?: boolean;
          error?: string;
          members?: MemberOption[];
        };
        if (!alive) return;
        if (res.ok && data.ok) {
          setMembers(Array.isArray(data.members) ? data.members : []);
        }
      } catch {
        if (alive) setMembers([]);
      }
    }
    loadMembers();
    return () => {
      alive = false;
    };
  }, []);

  const openItems = React.useMemo(() => items.filter((a) => a.status !== "done"), [items]);

  const now = new Date();
  const dueSoon = React.useMemo(() => {
    return openItems.filter((a) => {
      if (!a.dueDate) return false;
      const d = new Date(a.dueDate + "T00:00:00");
      const diffDays = (d.getTime() - now.getTime()) / (1000 * 60 * 60 * 24);
      return diffDays >= 0 && diffDays <= 3;
    });
  }, [openItems, now]);

  const overdue = React.useMemo(() => {
    return openItems.filter((a) => {
      if (!a.dueDate) return false;
      const d = new Date(a.dueDate + "T00:00:00");
      return d.getTime() < new Date(now.toDateString()).getTime();
    });
  }, [openItems, now]);

  const [modalOpen, setModalOpen] = React.useState(false);
  const [editing, setEditing] = React.useState<ActionItem | null>(null);

  const initialNew: ActionItem = React.useMemo(() => {
    const ts = new Date().toISOString();
    return {
      id: "",
      createdAt: ts,
      updatedAt: ts,
      machineId,
      reasonCode,
      hmDay,
      hmHour,
      title: "",
      notes: "",
      ownerUserId: null,
      ownerName: null,
      ownerEmail: null,
      dueDate: null,
      status: "open",
      priority: "medium",
    };
  }, [machineId, reasonCode, hmDay, hmHour]);

  const saveAction = useCallback(
    async (next: ActionItem, isNew: boolean) => {
      const payload = {
        machineId: next.machineId,
        reasonCode: next.reasonCode,
        hmDay: next.hmDay,
        hmHour: next.hmHour,
        title: next.title.trim(),
        notes: next.notes.trim(),
        ownerUserId: next.ownerUserId,
        dueDate: next.dueDate,
        status: next.status,
        priority: next.priority,
      };
      const url = isNew ? "/api/downtime/actions" : `/api/downtime/actions/${next.id}`;
      const res = await fetch(url, {
        method: isNew ? "POST" : "PATCH",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(payload),
      });
      const data = (await res.json().catch(() => ({}))) as {
        ok?: boolean;
        error?: string;
        action?: ActionItem;
      };
      if (!res.ok || !data.ok || !data.action) {
        return { ok: false, error: data.error || "Failed to save action" };
      }
      setItems((prev) => {
        if (isNew) return [data.action as ActionItem, ...prev];
        const i = prev.findIndex((x) => x.id === data.action?.id);
        if (i === -1) return [data.action as ActionItem, ...prev];
        const copy = [...prev];
        copy[i] = data.action as ActionItem;
        return copy;
      });
      return { ok: true };
    },
    []
  );

  const deleteAction = useCallback(async (id: string) => {
    const res = await fetch(`/api/downtime/actions/${id}`, { method: "DELETE" });
    const data = (await res.json().catch(() => ({}))) as { ok?: boolean; error?: string };
    if (!res.ok || !data.ok) {
      return { ok: false, error: data.error || "Failed to delete action" };
    }
    setItems((prev) => prev.filter((x) => x.id !== id));
    return { ok: true };
  }, []);

  const list = items
    .slice()
    .sort((a, b) => (a.status === "done" ? 1 : -1) - (b.status === "done" ? 1 : -1));

  return (
    <div className="mt-4">
      <div className="flex items-start justify-between gap-3">
        <div>
          <div className="text-xs text-zinc-400">
            Convert insights into ownership (who + when).
          </div>
        </div>
        <button
          onClick={() => {
            setEditing(null);
            setModalOpen(true);
          }}
          className="rounded-xl border border-white/10 bg-white/5 px-3 py-2 text-xs text-white hover:bg-white/10"
        >
          + New action
        </button>
      </div>

      {/* mini KPIs */}
      <div className="mt-4 grid grid-cols-3 gap-3">
        <div className="rounded-2xl border border-white/10 bg-black/20 p-3">
          <div className="text-[11px] text-zinc-400">Open</div>
          <div className="mt-1 text-base font-semibold text-white">{fmtNum(openItems.length, 0)}</div>
        </div>
        <div className="rounded-2xl border border-white/10 bg-black/20 p-3">
          <div className="text-[11px] text-zinc-400">Due soon (3d)</div>
          <div className="mt-1 text-base font-semibold text-white">{fmtNum(dueSoon.length, 0)}</div>
        </div>
        <div className="rounded-2xl border border-white/10 bg-black/20 p-3">
          <div className="text-[11px] text-zinc-400">Overdue</div>
          <div className="mt-1 text-base font-semibold text-white">{fmtNum(overdue.length, 0)}</div>
        </div>
      </div>

      {error ? (
        <div className="mt-3 rounded-xl border border-rose-500/25 bg-rose-500/10 px-3 py-2 text-xs text-rose-200">
          {error}
        </div>
      ) : null}

      <div className="mt-4 overflow-hidden rounded-2xl border border-white/10 bg-black/20">
        <div className="grid grid-cols-12 gap-2 border-b border-white/10 px-4 py-3 text-[11px] text-zinc-500">
          <div className="col-span-6">Action</div>
          <div className="col-span-3">Owner</div>
          <div className="col-span-3 text-right">Status</div>
        </div>

        {loading ? (
          <div className="p-4 text-sm text-zinc-400">Loading actions…</div>
        ) : list.length === 0 ? (
          <div className="p-4 text-sm text-zinc-400">
            No actions yet. Create one from the current selection (Reason / Heatmap / Machine).
          </div>
        ) : (
          list.slice(0, 8).map((a) => (
            <div
              key={a.id}
              className="grid grid-cols-12 gap-2 border-b border-white/5 px-4 py-3 hover:bg-white/5"
            >
              <button
                onClick={() => {
                  setEditing(a);
                  setModalOpen(true);
                }}
                className="col-span-6 text-left"
              >
                <div className="truncate text-sm text-white">{a.title || "Untitled action"}</div>
                <div className="mt-1 flex flex-wrap items-center gap-2 text-[11px] text-zinc-500">
                  {a.reasonCode ? (
                    <button
                      className="rounded-full border border-white/10 bg-white/5 px-2 py-0.5 hover:bg-white/10"
                      onClick={(e) => {
                        e.stopPropagation();
                        onFocusReason(a.reasonCode!);
                      }}
                      title="Focus this reason"
                    >
                      {a.reasonCode}
                    </button>
                  ) : null}
                  {a.dueDate ? <span>Due {new Date(a.dueDate).toLocaleDateString()}</span> : <span>No due date</span>}
                  <span className={cn("rounded-full border px-2 py-0.5", priorityPill(a.priority))}>
                    {a.priority}
                  </span>
                </div>
              </button>

              <div className="col-span-3 flex items-center text-sm text-zinc-200">
                {a.ownerName || a.ownerEmail || "—"}
              </div>

              <div className="col-span-3 flex items-center justify-end gap-2">
                <span className={cn("rounded-full border px-2 py-1 text-[11px]", statusPill(a.status))}>
                  {a.status.replace("_", " ")}
                </span>

                {a.status !== "done" ? (
                  <button
                    onClick={async () => {
                      const result = await saveAction(
                        { ...a, status: "done", updatedAt: new Date().toISOString() },
                        false
                      );
                      if (!result.ok) {
                        setError(result.error || "Failed to update action");
                      }
                    }}
                    className="rounded-lg border border-emerald-500/25 bg-emerald-500/10 px-2 py-1 text-[11px] text-emerald-200 hover:bg-emerald-500/15"
                    title="Mark done"
                  >
                    Done
                  </button>
                ) : null}
              </div>
            </div>
          ))
        )}

        {list.length > 8 ? (
          <div className="p-3 text-[11px] text-zinc-500">
            Showing 8 / {list.length}. (Later: pagination + filters)
          </div>
        ) : null}
      </div>

      <ActionModal
        open={modalOpen}
        onClose={() => setModalOpen(false)}
        initial={editing ?? initialNew}
        onSave={saveAction}
        onDelete={(id) => deleteAction(id)}
        members={members}
        isNew={!editing}
      />
    </div>
  );
}

export default function DowntimePageClient() {
  const sp = useSearchParams();
  const router = useRouter();
  const pathname = usePathname();

  // URL-backed filters
  const range = (sp.get("range") as Range) || "30d";
  const machineId = sp.get("machineId") || null;

  // client-only filters (shareable)
  const metric = ((sp.get("metric") as Metric) || "minutes") as Metric;
  const reasonCode = sp.get("reasonCode") || null;
  const shift = (sp.get("shift") || "all").toUpperCase();
  const planned = (sp.get("planned") as "all" | "planned" | "unplanned") || "all";
  const microstopLtMin = sp.get("microstopLtMin") || "2";

  const hmDay = sp.get("hmDay");
  const hmHour = sp.get("hmHour");

  const heatmapSel =
    hmDay != null && hmHour != null && isValidNum(hmDay) && isValidNum(hmHour)
      ? { day: Number(hmDay), hour: Number(hmHour) }
      : null;


  const [pareto, setPareto] = useState<ApiParetoRes | null>(null);
  const [loading, setLoading] = useState(true);
  const [err, setErr] = useState<string | null>(null);
  const [eventsRes, setEventsRes] = useState<ApiDowntimeEventsRes | null>(null);
  const [eventsLoading, setEventsLoading] = useState(false);
  const [eventsErr, setEventsErr] = useState<string | null>(null);
  const [catalogRows, setCatalogRows] = useState<ApiReasonCatalogRow[]>([]);
  const [catalogErr, setCatalogErr] = useState<string | null>(null);

  const [eventsLimit, setEventsLimit] = useState<number>(200);
  const [eventsBefore, setEventsBefore] = useState<string | null>(null);
  const debug = sp.get("debug") === "1";

  // simple client filter (fast): text search on machine/reason/wo
  const [eventSearch, setEventSearch] = useState("");


  const [drawer, setDrawer] = useState<{ open: boolean; row: MetricRow | null }>({
    open: false,
    row: null,
  });

  function fmtMxn(n: number) {
    return new Intl.NumberFormat("es-MX", {
        style: "currency",
        currency: "MXN",
        maximumFractionDigits: 0,
    }).format(n);
    }

  function setParams(patch: Record<string, string | null>) {
    const next = buildSearch(sp, patch);
    router.replace(`${pathname}?${next}`, { scroll: false });
  }
  const mxnPerMin = Number(sp.get("mxnPerMin") || "0");
    const [mxnPerMinInput, setMxnPerMinInput] = useState<string>(sp.get("mxnPerMin") ?? "");

    useEffect(() => {
    setMxnPerMinInput(String(mxnPerMin || ""));
    }, [mxnPerMin]);
  

  // Fetch (real)
  useEffect(() => {
    let alive = true;
    const ac = new AbortController();

    async function run() {
      setLoading(true);
      setErr(null);

      try {
        const qs = new URLSearchParams();
        qs.set("kind", "downtime");
        qs.set("range", range);
        if (machineId) qs.set("machineId", machineId);
        qs.set("shift", shift);
        qs.set("planned", planned);
        qs.set("microstopLtMin", microstopLtMin);

        const r1 = await fetch(`/api/analytics/pareto?${qs.toString()}`, {
          cache: "no-cache",
          credentials: "include",
          signal: ac.signal,
        });

        const j1raw = (await r1.json().catch(() => ({}))) as ApiParetoRes;

        if (!alive) return;

        if (!r1.ok || j1raw.ok === false) {
          setErr(j1raw?.error ?? "Failed to load pareto");
          setPareto(null);
          setLoading(false);
          return;
        }

        setPareto(normalizeParetoRes(j1raw));
        setLoading(false);
      } catch (e: any) {
        if (!alive) return;
        setErr(e?.message ?? "Network error");
        setLoading(false);
      }
    }

    run();
    return () => {
      alive = false;
      ac.abort();
    };
  }, [range, machineId, shift, planned, microstopLtMin]);

  useEffect(() => {
    let alive = true;
    const ac = new AbortController();

    async function run() {
      setCatalogErr(null);
      try {
        const res = await fetch("/api/reasons/catalog?kind=downtime", {
          cache: "no-cache",
          credentials: "include",
          signal: ac.signal,
        });
        const json = (await res.json().catch(() => ({}))) as ApiReasonCatalogRes;
        if (!alive) return;
        if (!res.ok || json.ok === false) {
          setCatalogRows([]);
          setCatalogErr(json.error ?? "Failed to load reason catalog");
          return;
        }
        setCatalogRows(Array.isArray(json.rows) ? json.rows : []);
      } catch (err: unknown) {
        if (!alive) return;
        setCatalogRows([]);
        setCatalogErr(err instanceof Error ? err.message : "Network error");
      }
    }

    run();
    return () => {
      alive = false;
      ac.abort();
    };
  }, []);

    useEffect(() => {
        let alive = true;
        const ac = new AbortController();

        async function run() {
            setEventsLoading(true);
            setEventsErr(null);

            try {
            const qs = new URLSearchParams();
            qs.set("range", range);
            qs.set("limit", String(eventsLimit));
            if (machineId) qs.set("machineId", machineId);
            if (reasonCode) qs.set("reasonCode", reasonCode);
            qs.set("shift", shift);
            qs.set("planned", planned);
            qs.set("microstopLtMin", microstopLtMin);
            if (eventsBefore) qs.set("before", eventsBefore);

            const r = await fetch(`/api/analytics/downtime-events?${qs.toString()}`, {
                cache: "no-cache",
                credentials: "include",
                signal: ac.signal,
            });

            const j = (await r.json().catch(() => ({}))) as ApiDowntimeEventsRes;
            if (!alive) return;

            if (!r.ok || j.ok === false) {
                setEventsErr(j?.error ?? "Failed to load events");
                setEventsRes(null);
                setEventsLoading(false);
                return;
            }

            setEventsRes(j);
            setEventsLoading(false);
            } catch (e: any) {
            if (!alive) return;
            setEventsErr(e?.message ?? "Network error");
            setEventsLoading(false);
            }
        }

        run();
        return () => {
            alive = false;
            ac.abort();
        };
        }, [range, machineId, reasonCode, shift, planned, microstopLtMin, eventsLimit, eventsBefore]);

  // Derived data
  const events = eventsRes?.events ?? [];
  const paretoEffective = useMemo(() => {
    const normalized = pareto ? normalizeParetoRes(pareto) : null;
    if (normalized?.rows && normalized.rows.length > 0) return normalized;
    const fromEvents = buildParetoFromEvents(events);
    if (!fromEvents) return normalized;
    return {
      ...fromEvents,
      range: (eventsRes?.range as any) ?? normalized?.range,
      start: eventsRes?.start ?? normalized?.start,
      orgId: eventsRes?.orgId ?? normalized?.orgId,
      machineId: eventsRes?.machineId ?? normalized?.machineId ?? null,
    };
  }, [pareto, events, eventsRes?.orgId, eventsRes?.machineId, eventsRes?.range, eventsRes?.start]);
  const usingEventsFallback = (paretoEffective?.rows?.length ?? 0) > 0 && (pareto?.rows?.length ?? 0) === 0 && events.length > 0;

  const baseRows = paretoEffective?.rows ?? [];
  const metricRowsAll = useMemo(() => computeMetricRows(baseRows, metric), [baseRows, metric]);

  const metricRowsFiltered = useMemo(() => {
    if (!reasonCode) return metricRowsAll;
    return metricRowsAll.filter((r) => r.reasonCode === reasonCode);
  }, [metricRowsAll, reasonCode]);

  const selectedReasonLabel = useMemo(() => {
    if (!reasonCode) return null;
    const fromMetrics = metricRowsAll.find((row) => row.reasonCode === reasonCode)?.reasonLabel;
    if (fromMetrics) return fromMetrics;
    const fromCatalog = catalogRows.find((row) => row.reasonCode === reasonCode)?.reasonLabel;
    return fromCatalog ?? reasonCode;
  }, [catalogRows, metricRowsAll, reasonCode]);

  const catalogByCategory = useMemo(() => {
    const grouped = new Map<string, { categoryLabel: string; rows: ApiReasonCatalogRow[] }>();
    for (const row of catalogRows) {
      const key = row.categoryId;
      const slot = grouped.get(key) ?? { categoryLabel: row.categoryLabel, rows: [] };
      slot.rows.push(row);
      grouped.set(key, slot);
    }
    return [...grouped.entries()].map(([categoryId, value]) => ({
      categoryId,
      categoryLabel: value.categoryLabel,
      rows: value.rows,
    }));
  }, [catalogRows]);

  const totalMinutes = paretoEffective?.totalMinutesLost ?? 0;
  const totalStops = useMemo(
    () => baseRows.reduce((acc, r) => acc + (r.count ?? 0), 0),
    [baseRows]
  );

  const top3Share = useMemo(() => {
    const top3 = metricRowsAll.slice(0, 3);
    return top3.reduce((acc, r) => acc + (r.pctOfTotal ?? 0), 0);
  }, [metricRowsAll]);

  const unclassifiedPct = useMemo(() => findUnclassifiedPct(metricRowsAll), [metricRowsAll]);

  const threshold80Index = useMemo(() => {
    // If API threshold80 exists, it’s based on minutes. For count metric, compute locally.
    if (metric === "minutes") return paretoEffective?.threshold80?.index ?? null;
    const idx = metricRowsAll.findIndex((r) => (r.cumulativePct ?? 0) >= 80);
    return idx >= 0 ? idx : null;
  }, [metric, paretoEffective?.threshold80?.index, metricRowsAll]);

  const heroData = useMemo(() => {
    // Keep hero readable: top 12 (like your screenshot)
    const slice = metricRowsAll.slice(0, 12);
    return slice.map((r, i) => ({
      i,
      code: r.reasonCode,
      label: r.reasonLabel,
      value: r.value,
      cum: r.cumulativePct,
      pct: r.pctOfTotal,
      count: r.count,
    }));
  }, [metricRowsAll]);

const totalDowntimeMin = paretoEffective?.totalMinutesLost ?? 0;

useEffect(() => {
  setEventsBefore(null);
}, [range, machineId, reasonCode, shift, planned, microstopLtMin]);

const filteredEvents = useMemo(() => {
  let list = events;

  // Heatmap filter (day/hour) — filters by overlap with that hour bucket
  if (heatmapSel) {
    list = list.filter((e) => eventTouchesSlot(e, heatmapSel.day, heatmapSel.hour));
  }

  const q = eventSearch.trim().toLowerCase();
  if (!q) return list;

  return list.filter((e) => {
    const hay = [
      e.machineName ?? "",
      e.reasonLabel ?? "",
      e.reasonCode ?? "",
      e.reasonText ?? "",
      e.workOrderId ?? "",
      e.episodeId ?? "",
    ]
      .join(" ")
      .toLowerCase();
    return hay.includes(q);
  });
}, [events, eventSearch, heatmapSel]);



// Use filtered pareto totals so top filters always affect the KPI.
const stops = totalStops;

// Window minutes for MTBF/Availability
const windowMin =
  range === "24h" ? 24 * 60 :
  range === "7d"  ? 7 * 24 * 60 :
  range === "30d" ? 30 * 24 * 60 : 0;

// Availability loss % (downtime / window)
const availabilityLossPct = windowMin > 0 ? (totalDowntimeMin / windowMin) * 100 : 0;

// MTTR proxy = avg stop duration
const mttrMin = stops > 0 ? totalDowntimeMin / stops : 0;

// MTBF proxy = avg run time between stops
const mtbfHours = stops > 0 ? (Math.max(0, windowMin - totalDowntimeMin) / stops) / 60 : 0;

// Impact (MXN) if rate is given
const rate = Number(mxnPerMinInput || "0");
const estImpactMxn = rate > 0 ? totalDowntimeMin * rate : 0;



  function exportCSV() {
    const rows = metricRowsAll;
    const header = [
    "reasonCode",
    "reasonLabel",
    metric === "minutes" ? "minutesLost" : "count",
    "stops",
    "pctOfTotal",
    "cumulativePct",
    ];
    const lines = [header.join(",")];

    rows.forEach((r) => {
      const v = metric === "minutes" ? (r.value ?? 0) : (r.value ?? 0);
      const cells = [
        `"${String(r.reasonCode ?? "").replaceAll('"', '""')}"`,
        `"${String(r.reasonLabel ?? "").replaceAll('"', '""')}"`,
        String(v),
        String(r.count ?? 0),
        String(r.pctOfTotal ?? 0),
        String(r.cumulativePct ?? 0),
      ];
      lines.push(cells.join(","));
    });

    const blob = new Blob([lines.join("\n")], { type: "text/csv;charset=utf-8" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = `downtime_pareto_${metric}_${range}${machineId ? `_machine_${machineId}` : ""}.csv`;
    a.click();
    URL.revokeObjectURL(url);
  }

  async function shareLink() {
    try {
      await navigator.clipboard.writeText(window.location.href);
      // silent (you can add a toast later)
    } catch {
      // ignore
    }
  }


  const scopeChips = (
    <div className="flex flex-wrap items-center gap-2">
      <span className="text-xs text-zinc-400">Scope:</span>
      <span className="rounded-full border border-emerald-500/25 bg-emerald-500/10 px-3 py-1 text-xs text-emerald-200">
        Org
      </span>
      {machineId ? (
        <span className="inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/5 px-3 py-1 text-xs text-white">
          Machine filtered
          <button
            className="rounded-full border border-white/10 bg-black/20 px-2 py-0.5 text-[11px] text-zinc-200 hover:bg-white/10"
            onClick={() => setParams({ machineId: null, reasonCode: null })}
          >
            ✕
          </button>
        </span>
      ) : (
        <span className="rounded-full border border-white/10 bg-white/5 px-3 py-1 text-xs text-zinc-200">
          All machines
        </span>
      )}
      {reasonCode ? (
        <span className="inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/5 px-3 py-1 text-xs text-white">
          Reason: {selectedReasonLabel ?? reasonCode}
          <button
            className="rounded-full border border-white/10 bg-black/20 px-2 py-0.5 text-[11px] text-zinc-200 hover:bg-white/10"
            onClick={() => setParams({ reasonCode: null })}
          >
            ✕
          </button>
        </span>
      ) : null}
      {heatmapSel ? (
        <span className="inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/5 px-3 py-1 text-xs text-white">
            Heatmap: {DAY_LABELS[heatmapSel.day]} {String(heatmapSel.hour).padStart(2, "0")}:00
            <button
            className="rounded-full border border-white/10 bg-black/20 px-2 py-0.5 text-[11px] text-zinc-200 hover:bg-white/10"
            onClick={() => setParams({ hmDay: null, hmHour: null })}
            >
            ✕
            </button>
        </span>
        ) : null}

    </div>
  );
  

    const filtersRow = (
    <div className="mt-4 flex items-center justify-between gap-4">
        {/* LEFT: range + metric + reset (never wrap) */}
        <div className="flex items-center gap-2 flex-nowrap overflow-x-auto no-scrollbar">
        <button
            onClick={() => setParams({ range: "24h" })}
            className={cn(
            "h-9 rounded-xl border px-3 text-xs",
            range === "24h"
                ? "border-emerald-500/30 bg-emerald-500/10 text-emerald-100"
                : "border-white/10 bg-white/5 text-zinc-200 hover:bg-white/10"
            )}
        >
            Today
        </button>
        <button
            onClick={() => setParams({ range: "7d" })}
            className={cn(
            "h-9 rounded-xl border px-3 text-xs",
            range === "7d"
                ? "border-emerald-500/30 bg-emerald-500/10 text-emerald-100"
                : "border-white/10 bg-white/5 text-zinc-200 hover:bg-white/10"
            )}
        >
            7D
        </button>
        <button
            onClick={() => setParams({ range: "30d" })}
            className={cn(
            "h-9 rounded-xl border px-3 text-xs",
            range === "30d"
                ? "border-emerald-500/30 bg-emerald-500/10 text-emerald-100"
                : "border-white/10 bg-white/5 text-zinc-200 hover:bg-white/10"
            )}
        >
            30D
        </button>

        <div className="mx-2 h-6 w-px bg-white/10" />

        <button
            onClick={() => setParams({ metric: "minutes" })}
            className={cn(
            "h-9 rounded-xl border px-3 text-xs",
            metric === "minutes"
                ? "border-white/10 bg-white/10 text-white"
                : "border-white/10 bg-white/5 text-zinc-200 hover:bg-white/10"
            )}
        >
            Minutes
        </button>
        <button
            onClick={() => setParams({ metric: "count" })}
            className={cn(
            "h-9 rounded-xl border px-3 text-xs",
            metric === "count"
                ? "border-white/10 bg-white/10 text-white"
                : "border-white/10 bg-white/5 text-zinc-200 hover:bg-white/10"
            )}
        >
            Count
        </button>

        <div className="mx-2 h-6 w-px bg-white/10" />

        <button
            onClick={() =>
            setParams({
                range: "30d",
                metric: "minutes",
                shift: "all",
                planned: "all",
                microstopLtMin: "2",
                reasonCode: null,
                mxnPerMin: null,
            })
            }
            className="h-9 rounded-xl border border-white/10 bg-white/5 px-3 text-xs text-zinc-200 hover:bg-white/10"
        >
            Reset filters
        </button>
        </div>

        {/* RIGHT: shift + planned/unplanned + microstop (also never wrap) */}
        <div className="flex items-center gap-2 flex-nowrap overflow-x-auto no-scrollbar">
        <select
            value={shift}
            onChange={(e) => setParams({ shift: e.target.value })}
            className="h-9 rounded-xl border border-white/10 bg-white/5 px-3 text-xs text-zinc-200 outline-none hover:bg-white/10"
        >
            <option value="all">All shifts</option>
            <option value="A">Shift A</option>
            <option value="B">Shift B</option>
            <option value="C">Shift C</option>
        </select>

        <select
            value={planned}
            onChange={(e) => setParams({ planned: e.target.value })}
            className="h-9 rounded-xl border border-white/10 bg-white/5 px-3 text-xs text-zinc-200 outline-none hover:bg-white/10"
        >
            <option value="all">Planned + Unplanned</option>
            <option value="planned">Planned</option>
            <option value="unplanned">Unplanned</option>
        </select>

        <div className="flex h-9 items-center gap-2 rounded-xl border border-white/10 bg-white/5 px-3 text-xs text-zinc-200">
            <span className="text-zinc-400">Microstop &lt;</span>
            <input
            value={microstopLtMin}
            onChange={(e) => setParams({ microstopLtMin: e.target.value })}
            className="w-10 bg-transparent text-right text-xs text-white outline-none"
            />
            <span className="text-zinc-400">min</span>
        </div>
        </div>
    </div>
    );

  function HeroTooltip({
    active,
    payload,
  }: {
    active?: boolean;
    payload?: Array<{ payload?: any }>;
  }) {
    if (!active || !payload?.length) return null;
    const p = payload[0]?.payload;
    if (!p) return null;

    return (
      <div className="rounded-xl border border-white/10 bg-zinc-950/95 px-4 py-3 shadow-lg">
        <div className="text-sm font-semibold text-white">{p.label}</div>
        <div className="mt-2 space-y-1 text-xs text-zinc-300">
          <div>
            Value:{" "}
            <span className="text-white">
              {metric === "minutes" ? `${fmtNum(p.value, 1)} min` : fmtNum(p.value, 0)}
            </span>
          </div>
          <div>
            Share: <span className="text-white">{fmtPct(p.pct, 1)}</span>
          </div>
          <div>
            Stops: <span className="text-white">{fmtNum(p.count, 0)}</span>
          </div>
          <div>
            Cumulative: <span className="text-white">{fmtPct(p.cum, 0)}</span>
          </div>
        </div>
      </div>
    );
  }

  return (
    <div className="p-4 sm:p-6">
      {/* Header */}
      <div className="relative overflow-hidden rounded-3xl border border-white/10 bg-zinc-950/40 p-6 backdrop-blur-xl">
        <div
          className="pointer-events-none absolute inset-0 opacity-70"
          style={{
            background:
              "radial-gradient(900px 500px at 20% 10%, rgba(16,185,129,.18), transparent 60%)," +
              "radial-gradient(900px 500px at 85% 30%, rgba(59,130,246,.12), transparent 60%)," +
              "radial-gradient(900px 600px at 50% 100%, rgba(244,63,94,.10), transparent 60%)",
          }}
        />

        <div className="relative flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
          <div className="min-w-0">
            <div className="text-2xl font-semibold text-white">Downtime Pareto — Full Report</div>
            <div className="mt-1 text-sm text-zinc-400">
              Analyze downtime patterns and prioritize improvements
            </div>

            <div className="mt-4">{scopeChips}</div>
            {filtersRow}
          </div>

          <div className="relative flex flex-wrap items-center gap-2 lg:justify-end">
            <div className="flex items-center gap-2 rounded-xl border border-white/10 bg-white/5 px-3 py-2">
            <span className="text-xs text-zinc-400">MXN/min</span>
            <input
                value={mxnPerMinInput}
                onChange={(e) => setMxnPerMinInput(e.target.value.replace(/[^\d]/g, ""))}
                onBlur={() => setParams({ mxnPerMin: mxnPerMinInput ? mxnPerMinInput : null })}
                placeholder="0"
                className="w-20 bg-transparent text-right text-sm text-white outline-none"
            />
            </div>

            <button
              onClick={exportCSV}
              className="rounded-xl border border-white/10 bg-white/5 px-4 py-2 text-sm text-white hover:bg-white/10"
            >
              Export
            </button>
            <button
              onClick={shareLink}
              className="rounded-xl border border-white/10 bg-white/5 px-4 py-2 text-sm text-white hover:bg-white/10"
            >
              Share
            </button>

            <span className="rounded-xl border border-white/10 bg-white/5 px-4 py-2 text-sm text-zinc-400">
              Plant select (soon)
            </span>

            {machineId ? (
              <Link
                href={`/machines/${encodeURIComponent(machineId)}`}
                className="rounded-xl border border-emerald-500/30 bg-emerald-500/10 px-4 py-2 text-sm text-emerald-100 hover:bg-emerald-500/20"
              >
                Back to machine →
              </Link>
            ) : null}
          </div>
        </div>
      </div>

      {/* Loading / error */}
      {loading ? (
        <div className="mt-6 text-sm text-zinc-400">Loading downtime pareto…</div>
      ) : err ? (
        <div className="mt-6 rounded-2xl border border-red-500/20 bg-red-500/10 p-4 text-sm text-red-200">
          {err}
        </div>
      ) : null}

      {debug ? (
        <div className="mt-6 rounded-2xl border border-white/10 bg-black/30 p-4 text-xs text-zinc-300">
          <div className="flex flex-wrap items-center justify-between gap-3">
            <div className="font-semibold text-white">Debug</div>
            <div className="text-[11px] text-zinc-500">
              Disable with <span className="text-zinc-300">debug=0</span>
            </div>
          </div>
          <div className="mt-2 grid grid-cols-1 gap-2 sm:grid-cols-2 xl:grid-cols-4">
            <div className="rounded-xl border border-white/10 bg-white/5 p-3">
              <div className="text-[11px] text-zinc-500">Status</div>
              <div className="mt-1 text-zinc-200">
                loading={String(loading)} · err={err ?? "null"} · eventsLoading={String(eventsLoading)} · eventsErr=
                {eventsErr ?? "null"}
              </div>
            </div>
            <div className="rounded-xl border border-white/10 bg-white/5 p-3">
              <div className="text-[11px] text-zinc-500">Filters</div>
              <div className="mt-1 text-zinc-200">
                range={range} · machineId={machineId ?? "null"} · reasonCode={reasonCode ?? "null"}
              </div>
            </div>
            <div className="rounded-xl border border-white/10 bg-white/5 p-3">
              <div className="text-[11px] text-zinc-500">API payload sizes</div>
              <div className="mt-1 text-zinc-200">
                pareto.rows={(pareto?.rows?.length ?? 0)} · events={(eventsRes?.events?.length ?? 0)}
              </div>
            </div>
            <div className="rounded-xl border border-white/10 bg-white/5 p-3">
              <div className="text-[11px] text-zinc-500">Effective (used by UI)</div>
              <div className="mt-1 text-zinc-200">
                rows={(paretoEffective?.rows?.length ?? 0)} · usingEventsFallback={String(usingEventsFallback)}
              </div>
            </div>
          </div>
        </div>
      ) : null}

      {!loading && !err && (
        <>
          {eventsErr ? (
            <div className="mt-6 rounded-2xl border border-amber-500/20 bg-amber-500/10 p-4 text-sm text-amber-100">
              Events list unavailable: {eventsErr}
            </div>
          ) : null}
          {/* KPI strip */}
          <div className="mt-6 grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-8">
            <KPI
                label="Total downtime"
                value={fmtHoursFromMinutes(totalDowntimeMin)}
                sub={`${fmtNum(totalDowntimeMin, 1)} min`}
                accent="emerald"
            />
            <KPI
                label="Stops count"
                value={fmtNum(stops, 0)}
                sub="Distinct episodes (filtered)"
                accent="zinc"
            />
            <KPI
                label="Top reason share"
                value={metricRowsAll[0] ? fmtPct(metricRowsAll[0].pctOfTotal, 1) : "—"}
                sub={metricRowsAll[0] ? metricRowsAll[0].reasonLabel : ""}
                accent="yellow"
            />
            <KPI
                label="MTBF"
                value={stops > 0 ? `${fmtNum(mtbfHours, 1)} hrs` : "—"}
                sub="Proxy (window-based)"
            />
            <KPI
                label="MTTR"
                value={stops > 0 ? `${fmtNum(mttrMin, 1)} min` : "—"}
                sub="Avg stop duration"
            />
            <KPI
                label="Availability loss"
                value={windowMin > 0 ? `${fmtNum(availabilityLossPct, 1)}%` : "—"}
                sub="Downtime / window"
                accent="rose"
            />
            <KPI
                label="Est. impact (MXN)"
                value={rate > 0 ? fmtMxn(estImpactMxn) : "—"}
                sub={rate > 0 ? `Rate: ${fmtMxn(rate)}/min` : "Set MXN/min"}
                accent="rose"
            />
            <KPI
                label="Unclassified"
                value={`${fmtNum(unclassifiedPct, 0)}%`}
                sub="Data quality signal"
            />
            </div>


          {/* Hero + breakdown */}
          <div className="mt-6 grid grid-cols-1 gap-4 xl:grid-cols-3">
            {/* Hero chart */}
            <div className="rounded-3xl border border-white/10 bg-white/5 p-5 xl:col-span-2">
              <div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
                <div>
                  <div className="text-lg font-semibold text-white">Downtime Pareto Analysis</div>
                  <div className="mt-1 text-xs text-zinc-400">
                    Top reasons by impact · {range} · metric: {metric}
                  </div>
                </div>

                <div className="rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-xs text-zinc-300">
                  <div className="text-white">
                    Top 3 reasons explain{" "}
                    <span className="font-semibold">{fmtPct(top3Share, 1)}</span>
                  </div>
                  <div className="mt-1 text-zinc-400">
                    Fix these first = highest ROI
                  </div>
                </div>
              </div>

              <div
                className="mt-4 h-[360px] rounded-3xl border border-white/10 bg-black/30 p-4 backdrop-blur"
                style={{ boxShadow: "var(--app-chart-shadow)" }}
              >
                <ResponsiveContainer width="100%" height="100%" minHeight={200}>
                  <ComposedChart
                    data={heroData}
                    onClick={(st: any) => {
                      const p = st?.activePayload?.[0]?.payload;
                      if (!p?.code) return;
                      setParams({ reasonCode: p.code });
                    }}
                  >
                    <CartesianGrid strokeDasharray="3 3" stroke="var(--app-chart-grid)" />
                    <XAxis
                      dataKey="label"
                      interval={0}
                      tick={{ fill: "var(--app-chart-tick)" }}
                      tickFormatter={(v: string) => (v.length > 14 ? `${v.slice(0, 14)}…` : v)}
                    />
                    <YAxis
                      yAxisId="left"
                      tick={{ fill: "var(--app-chart-tick)" }}
                      tickFormatter={(v: number) =>
                        metric === "minutes" ? `${v}` : `${v}`
                      }
                    />
                    <YAxis
                      yAxisId="right"
                      orientation="right"
                      domain={[0, 100]}
                      tick={{ fill: "var(--app-chart-tick)" }}
                      tickFormatter={(v: number) => `${v}%`}
                    />
                    <Tooltip content={<HeroTooltip />} cursor={{ stroke: "var(--app-chart-grid)" }} />

                    <Bar
                      yAxisId="left"
                      dataKey="value"
                      radius={[10, 10, 0, 0]}
                      isAnimationActive={false}
                      fill="rgba(16,185,129,0.85)"
                    />
                    <Line
                      yAxisId="right"
                      type="monotone"
                      dataKey="cum"
                      stroke="rgba(110,231,183,0.95)"
                      strokeWidth={2}
                      dot={false}
                      isAnimationActive={false}
                    />
                    <ReferenceLine
                      yAxisId="right"
                      y={80}
                      stroke="rgba(255,255,255,0.25)"
                      strokeDasharray="6 6"
                    />
                  </ComposedChart>
                </ResponsiveContainer>
              </div>

              {threshold80Index != null && metricRowsAll[threshold80Index] ? (
                <div className="mt-3 text-xs text-zinc-400">
                  80% threshold reached at{" "}
                  <span className="text-white">{metricRowsAll[threshold80Index].reasonLabel}</span>
                </div>
              ) : null}
            </div>

            {/* Reason breakdown */}
            <div className="rounded-3xl border border-white/10 bg-white/5 p-5">
              <div className="flex items-start justify-between gap-3">
                <div>
                  <div className="text-lg font-semibold text-white">Reason Breakdown</div>
                  <div className="mt-1 text-xs text-zinc-400">Click row for details</div>
                </div>
                <div className="rounded-xl border border-white/10 bg-white/5 px-3 py-1.5 text-xs text-zinc-300">
                  Top {Math.min(12, metricRowsAll.length)}
                </div>
              </div>

              <div className="mt-4 rounded-2xl border border-white/10 bg-black/20 p-3">
                <div className="text-xs font-semibold text-white">Downtime reason menu</div>
                <div className="mt-1 text-[11px] text-zinc-400">
                  From settings or `downtime_menu.md` fallback
                </div>
                {catalogErr ? (
                  <div className="mt-2 text-[11px] text-rose-300">{catalogErr}</div>
                ) : null}
                <div className="mt-3 max-h-[180px] space-y-2 overflow-y-auto no-scrollbar pr-1">
                  {catalogByCategory.map((group) => (
                    <div key={group.categoryId} className="rounded-xl border border-white/10 bg-white/5 p-2">
                      <div className="mb-1 text-[11px] font-semibold text-zinc-300">{group.categoryLabel}</div>
                      <div className="flex flex-wrap gap-1.5">
                        {group.rows.map((option) => {
                          const active = reasonCode === option.reasonCode;
                          return (
                            <button
                              key={option.reasonCode}
                              onClick={() => setParams({ reasonCode: option.reasonCode })}
                              className={cn(
                                "rounded-lg border px-2 py-1 text-[11px]",
                                active
                                  ? "border-emerald-500/40 bg-emerald-500/15 text-emerald-200"
                                  : "border-white/10 bg-black/20 text-zinc-300 hover:bg-white/10"
                              )}
                            >
                              {option.detailLabel}
                            </button>
                          );
                        })}
                      </div>
                    </div>
                  ))}
                  {!catalogErr && catalogByCategory.length === 0 ? (
                    <div className="text-[11px] text-zinc-500">No reason menu available.</div>
                  ) : null}
                </div>
              </div>

              <div className="mt-4 max-h-[360px] overflow-y-auto no-scrollbar rounded-2xl border border-white/10 bg-black/20">
                <div className="grid grid-cols-12 gap-2 border-b border-white/10 px-4 py-3 text-[11px] text-zinc-500">
                  <div className="col-span-8">Reason</div>
                  <div className="col-span-4 text-right">{metric === "minutes" ? "Minutes" : "Count"}</div>
                </div>

                {metricRowsAll.slice(0, 12).map((r) => {
                  const active = reasonCode === r.reasonCode;
                  return (
                    <button
                      key={r.reasonCode}
                      className={cn(
                        "grid w-full grid-cols-12 gap-2 px-4 py-3 text-left text-sm transition",
                        "border-b border-white/5 hover:bg-white/5",
                        active && "bg-emerald-500/10"
                      )}
                      onClick={() => {
                        setDrawer({ open: true, row: r });
                        setParams({ reasonCode: r.reasonCode });
                      }}
                    >
                      <div className="col-span-8">
                        <div className="truncate text-white">{r.reasonLabel}</div>
                        <div className="mt-1 text-[11px] text-zinc-400">
                          {fmtPct(r.pctOfTotal, 1)} · {fmtNum(r.count, 0)} stops
                        </div>
                      </div>
                      <div className="col-span-4 text-right">
                        <div className="text-white">
                          {metric === "minutes" ? `${fmtNum(r.value, 1)}m` : fmtNum(r.value, 0)}
                        </div>
                        <div className="mt-1 text-[11px] text-zinc-500">
                          cum {fmtPct(r.cumulativePct, 0)}
                        </div>
                      </div>
                    </button>
                  );
                })}

                {metricRowsAll.length === 0 ? (
                  <div className="p-4 text-sm text-zinc-400">No data for this range.</div>
                ) : null}
              </div>

              {/* Coverage mini */}
              <div className="mt-4 rounded-2xl border border-white/10 bg-white/5 p-4">
                <div className="text-sm font-semibold text-white">Filtered downtime summary</div>
                <div className="mt-1 text-xs text-zinc-400">
                  Reflects the active range/machine/shift/planned/microstop filters
                </div>

                <div className="mt-3 grid grid-cols-2 gap-3">
                  <div className="rounded-xl border border-white/10 bg-black/20 p-3">
                    <div className="text-[11px] text-zinc-400">Episodes</div>
                    <div className="mt-1 text-base font-semibold text-white">
                      {fmtNum(stops, 0)}
                    </div>
                  </div>
                  <div className="rounded-xl border border-white/10 bg-black/20 p-3">
                    <div className="text-[11px] text-zinc-400">Minutes</div>
                    <div className="mt-1 text-base font-semibold text-white">
                      {fmtNum(totalDowntimeMin, 1)}
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>
          

          {/* Drilldown table */}
          <div className="mt-6 rounded-3xl border border-white/10 bg-white/5 p-5">
            <div className="flex flex-col gap-2 sm:flex-row sm:items-start sm:justify-between">
              <div>
                <div className="text-lg font-semibold text-white">Drilldown table</div>
                <div className="mt-1 text-xs text-zinc-400">
                  Sortable later · click row opens drawer
                </div>
              </div>
              <div className="text-xs text-zinc-400">
                Showing {metricRowsFiltered.length} / {metricRowsAll.length}
              </div>
            </div>

            <div className="mt-4 overflow-x-auto rounded-2xl border border-white/10 bg-black/20">
              <table className="w-full min-w-[860px] text-left text-sm">
                <thead className="border-b border-white/10 text-[11px] text-zinc-500">
                  <tr>
                    <th className="px-4 py-3">Reason</th>
                    <th className="px-4 py-3 text-right">Downtime (min)</th>
                    <th className="px-4 py-3 text-right">Stops</th>
                    <th className="px-4 py-3 text-right">Avg (min)</th>
                    <th className="px-4 py-3 text-right">% share</th>
                    <th className="px-4 py-3 text-right">Cum %</th>
                  </tr>
                </thead>
                <tbody className="divide-y divide-white/5">
                  {metricRowsFiltered.map((r) => {
                    const avg =
                      r.count > 0 && r.minutesLost != null ? r.minutesLost / r.count : null;

                    return (
                      <tr
                        key={r.reasonCode}
                        className={cn(
                          "cursor-pointer hover:bg-white/5",
                          reasonCode === r.reasonCode && "bg-emerald-500/10"
                        )}
                        onClick={() => {
                          setDrawer({ open: true, row: r });
                          setParams({ reasonCode: r.reasonCode });
                        }}
                      >
                        <td className="px-4 py-3">
                          <div className="truncate text-white">{r.reasonLabel}</div>
                          <div className="mt-1 text-[11px] text-zinc-500">{r.reasonCode}</div>
                        </td>
                        <td className="px-4 py-3 text-right text-white">
                          {r.minutesLost != null ? fmtNum(r.minutesLost, 1) : "—"}
                        </td>
                        <td className="px-4 py-3 text-right text-white">{fmtNum(r.count, 0)}</td>
                        <td className="px-4 py-3 text-right text-zinc-200">
                          {avg == null ? "—" : fmtNum(avg, 1)}
                        </td>
                        <td className="px-4 py-3 text-right text-zinc-200">{fmtPct(r.pctOfTotal, 1)}</td>
                        <td className="px-4 py-3 text-right text-zinc-200">{fmtPct(r.cumulativePct, 0)}</td>
                      </tr>
                    );
                  })}

                  {metricRowsFiltered.length === 0 ? (
                    <tr>
                      <td className="px-4 py-6 text-sm text-zinc-400" colSpan={6}>
                        No rows.
                      </td>
                    </tr>
                  ) : null}
                </tbody>
              </table>
            </div>
          </div>

          {/* Patterns + Events + Actions (layout placeholders, no endpoints yet) */}
          <div className="mt-6 grid grid-cols-1 gap-4 xl:grid-cols-3">
            <div className="rounded-3xl border border-white/10 bg-white/5 p-5 xl:col-span-2">
              <div className="text-lg font-semibold text-white">Patterns (heatmaps)</div>
              <div className="mt-1 text-xs text-zinc-400">
                Add endpoints later: hour-of-day × day heatmap, shift comparisons
              </div>
              <Heatmap
                events={events}
                metric={metric}
                selected={heatmapSel}
                onSelect={(day, hour) => setParams({ hmDay: String(day), hmHour: String(hour) })}
                onClear={() => setParams({ hmDay: null, hmHour: null })}
                />

            </div>
            <div className="rounded-3xl border border-white/10 bg-white/5 p-5">
              <div className="text-lg font-semibold text-white">Actions & ownership</div>
              <div className="mt-1 text-xs text-zinc-400">
                Next: create action from reason/event (owner, due date, status)
              </div>
              <ActionsOwnershipPanel
                machineId={machineId}
                reasonCode={reasonCode}
                heatmapSel={heatmapSel}
                onFocusReason={(code) => setParams({ reasonCode: code })}
                />

            </div>
          </div>

          <div className="mt-6 rounded-3xl border border-white/10 bg-white/5 p-5">
            <div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
                <div>
                <div className="text-lg font-semibold text-white">Event list (audit trail)</div>
                <div className="mt-1 text-xs text-zinc-400">
                    Real downtime episodes · filtered by scope + reason
                </div>
                </div>

                <div className="flex flex-wrap items-center gap-2">
                <input
                    value={eventSearch}
                    onChange={(e) => setEventSearch(e.target.value)}
                    placeholder="Search machine / reason / WO / episode…"
                    className="h-9 w-[260px] rounded-xl border border-white/10 bg-black/20 px-3 text-xs text-white outline-none placeholder:text-zinc-500"
                />

                <select
                    value={String(eventsLimit)}
                    onChange={(e) => {
                    setEventsBefore(null);
                    setEventsLimit(Number(e.target.value));
                    }}
                    className="h-9 rounded-xl border border-white/10 bg-white/5 px-3 text-xs text-zinc-200 outline-none hover:bg-white/10"
                >
                    <option value="50">50</option>
                    <option value="100">100</option>
                    <option value="200">200</option>
                    <option value="300">300</option>
                    <option value="500">500</option>
                </select>

                <button
                    onClick={() => setEventsBefore(null)}
                    className="h-9 rounded-xl border border-white/10 bg-white/5 px-3 text-xs text-zinc-200 hover:bg-white/10"
                >
                    Newest
                </button>

                <button
                    disabled={!eventsRes?.nextBefore}
                    onClick={() => setEventsBefore(eventsRes?.nextBefore ?? null)}
                    className={cn(
                    "h-9 rounded-xl border px-3 text-xs",
                    eventsRes?.nextBefore
                        ? "border-white/10 bg-white/5 text-zinc-200 hover:bg-white/10"
                        : "border-white/10 bg-white/5 text-zinc-500 opacity-50 cursor-not-allowed"
                    )}
                >
                    Older →
                </button>
                </div>
            </div>

            {eventsLoading ? (
                <div className="mt-4 text-sm text-zinc-400">Loading events…</div>
            ) : eventsErr ? (
                <div className="mt-4 rounded-2xl border border-red-500/20 bg-red-500/10 p-4 text-sm text-red-200">
                {eventsErr}
                </div>
            ) : (
                <div className="mt-4 overflow-x-auto rounded-2xl border border-white/10 bg-black/20">
                <table className="w-full min-w-[980px] text-left text-sm">
                    <thead className="border-b border-white/10 text-[11px] text-zinc-500">
                    <tr>
                        <th className="px-4 py-3">Start</th>
                        <th className="px-4 py-3">End</th>
                        <th className="px-4 py-3">Machine</th>
                        <th className="px-4 py-3">Reason</th>
                        <th className="px-4 py-3">WO</th>
                        <th className="px-4 py-3 text-right">Duration</th>
                        <th className="px-4 py-3 text-right">Episode</th>
                    </tr>
                    </thead>

                    <tbody className="divide-y divide-white/5">
                    {filteredEvents.map((e) => {
                        const isActive = reasonCode === e.reasonCode;
                        const durMin = e.durationMinutes ?? (e.durationSeconds != null ? e.durationSeconds / 60 : null);

                        return (
                        <tr
                            key={e.id}
                            className={cn(
                            "cursor-pointer hover:bg-white/5",
                            isActive && "bg-emerald-500/10"
                            )}
                            onClick={() => {
                            // clicking an event focuses the rest of the page on its reason
                            setParams({ reasonCode: e.reasonCode });
                            }}
                            title="Click to focus this reason"
                        >
                            <td className="px-4 py-3 text-zinc-200">{fmtDT(e.startAt)}</td>
                            <td className="px-4 py-3 text-zinc-200">{fmtDT(e.endAt)}</td>
                            <td className="px-4 py-3">
                            <div className="truncate text-white">{e.machineName ?? "—"}</div>
                            <div className="mt-1 text-[11px] text-zinc-500">{e.machineId}</div>
                            </td>
                            <td className="px-4 py-3">
                            <div className="truncate text-white">{e.reasonLabel}</div>
                            <div className="mt-1 text-[11px] text-zinc-500">{e.reasonCode}</div>
                            {e.reasonText && e.reasonText !== e.reasonLabel ? (
                              <div className="mt-1 text-[11px] text-zinc-400">{e.reasonText}</div>
                            ) : null}
                            </td>
                            <td className="px-4 py-3 text-zinc-200">{e.workOrderId ?? "—"}</td>
                            <td className="px-4 py-3 text-right text-white">
                            {durMin == null ? "—" : `${fmtNum(durMin, 1)} min`}
                            </td>
                            <td className="px-4 py-3 text-right text-[11px] text-zinc-500">
                            {e.episodeId ?? "—"}
                            </td>
                        </tr>
                        );
                    })}

                    {filteredEvents.length === 0 ? (
                        <tr>
                        <td className="px-4 py-6 text-sm text-zinc-400" colSpan={7}>
                            No events found for this filter/range.
                        </td>
                        </tr>
                    ) : null}
                    </tbody>
                </table>
                </div>
            )}

            <div className="mt-3 text-[11px] text-zinc-500">
                Tip: click any row to focus the whole page on that reason (Pareto + table + drawer).
            </div>
            </div>

        </>
      )}

      <ReasonDrawer
        open={drawer.open}
        onClose={() => setDrawer({ open: false, row: null })}
        row={drawer.row}
        metric={metric}
      />
    </div>
  );
}
