import {
  ActionIcon,
  Alert,
  Avatar,
  Badge,
  Box,
  Button,
  Card as MCard,
  Divider,
  Group,
  Loader,
  Modal,
  Paper,
  ScrollArea,
  Select,
  SimpleGrid,
  Stack,
  Table,
  Text,
  Textarea,
  Title,
  Tooltip,
  UnstyledButton,
} from "@mantine/core";
import { BarChart } from "@mantine/charts";
import {
  IconAlertTriangle,
  IconArrowBackUp,
  IconCalendarStats,
  IconCheck,
  IconClock,
  IconDownload,
  IconHourglass,
  IconLock,
  IconPlus,
  IconRefresh,
  IconSettings,
  IconSparkles,
  IconTrendingUp,
} from "@tabler/icons-react";
import { useEffect, useMemo, useState } from "react";
import {
  dailyReport,
  generateDailySummary,
  getDailySummary,
  getSetting,
  setSetting,
  type DailyReport as Report,
  type DailySummary,
} from "../api";
import { formatDuration } from "./format";
import { tagColor } from "./colors";

interface Props {
  date: string; // YYYY-MM-DD
  onJumpToCard?: (cardId: string) => void;
}

const PROMPT_KEY = "daily_report_prompt";
const PROMPT_DEFAULT =
  "Eres un coach de equipo. Resume el reporte diario en un MAXIMO de 4 frases cortas, mencionando: (1) total de tareas hechas y quien destaco, (2) cualquier card reabierta o deadline vencido que merezca atencion, (3) cards estancadas criticas (30+ dias) si las hay, (4) una frase corta de animo o aviso si toca. Tono natural, primera persona del plural, sin emojis. No inventes datos; usa solo los del JSON del reporte.";

function fmtDate(s: string): string {
  try {
    const d = new Date(s + "T00:00:00");
    return d.toLocaleDateString("es-ES", {
      weekday: "long",
      day: "2-digit",
      month: "long",
      year: "numeric",
    });
  } catch {
    return s;
  }
}

function KPI({
  label,
  value,
  color,
  icon,
  sub,
}: {
  label: string;
  value: string | number;
  color?: string;
  icon?: React.ReactNode;
  sub?: string;
}) {
  return (
    <Paper p="sm" withBorder radius="md">
      <Group gap={6} mb={2} align="center">
        {icon}
        <Text size="xs" c="dimmed" tt="uppercase" fw={600}>
          {label}
        </Text>
      </Group>
      <Text fz={28} fw={700} c={color}>
        {value}
      </Text>
      {sub && (
        <Text size="xs" c="dimmed">
          {sub}
        </Text>
      )}
    </Paper>
  );
}

function RankingList<T extends { name: string; count: number; user_id?: string }>({
  title,
  rows,
  emptyText,
  withAvatar = false,
}: {
  title: string;
  rows: T[];
  emptyText: string;
  withAvatar?: boolean;
}) {
  return (
    <MCard withBorder radius="md" p="sm">
      <Text fw={600} size="sm" mb={6}>
        {title}
      </Text>
      {rows.length === 0 ? (
        <Text size="xs" c="dimmed">
          {emptyText}
        </Text>
      ) : (
        <Stack gap={4}>
          {rows.map((r, i) => (
            <Group key={(r.user_id || r.name) + i} gap={6} wrap="nowrap" justify="space-between">
              <Group gap={6} wrap="nowrap" style={{ minWidth: 0, flex: 1 }}>
                {withAvatar && (
                  <Avatar size={22} radius="xl" color={tagColor(r.name || String(i))}>
                    {(r.name || "?").slice(0, 2).toUpperCase()}
                  </Avatar>
                )}
                <Text size="sm" truncate>
                  {r.name || "(sin nombre)"}
                </Text>
              </Group>
              <Badge size="sm" variant="light" color={i === 0 ? "teal" : "gray"}>
                {r.count}
              </Badge>
            </Group>
          ))}
        </Stack>
      )}
    </MCard>
  );
}

export function DailyReportView({ date, onJumpToCard }: Props) {
  const [data, setData] = useState<Report | null>(null);
  const [err, setErr] = useState<string | null>(null);
  const [summary, setSummary] = useState<DailySummary | null>(null);
  const [summaryLoading, setSummaryLoading] = useState(false);
  const [summaryErr, setSummaryErr] = useState<string | null>(null);
  const [settingsOpen, setSettingsOpen] = useState(false);
  const [promptDraft, setPromptDraft] = useState("");
  const [filterRequester, setFilterRequester] = useState<string | null>(null);
  const [filterAssignee, setFilterAssignee] = useState<string | null>(null);

  useEffect(() => {
    setData(null);
    setErr(null);
    dailyReport(date)
      .then(setData)
      .catch((e) => setErr((e as Error).message));
    setSummary(null);
    setSummaryErr(null);
    getDailySummary(date)
      .then((s) => setSummary(s.exists ? s : null))
      .catch(() => {});
  }, [date]);

  const regenerateSummary = async () => {
    setSummaryLoading(true);
    setSummaryErr(null);
    try {
      const s = await generateDailySummary(date);
      setSummary({ ...s, exists: true });
    } catch (e) {
      setSummaryErr((e as Error).message);
    } finally {
      setSummaryLoading(false);
    }
  };

  const openSettings = async () => {
    try {
      const s = await getSetting(PROMPT_KEY);
      setPromptDraft(s.value || PROMPT_DEFAULT);
    } catch {
      setPromptDraft(PROMPT_DEFAULT);
    }
    setSettingsOpen(true);
  };

  const saveSettings = async () => {
    await setSetting(PROMPT_KEY, promptDraft);
    setSettingsOpen(false);
  };

  const resetSettings = () => setPromptDraft(PROMPT_DEFAULT);

  const hourlyChartData = useMemo(() => {
    if (!data) return [];
    return data.hourly_moves.map((n, h) => ({
      hora: String(h).padStart(2, "0") + ":00",
      movimientos: n,
    }));
  }, [data]);

  const requesterOptions = useMemo(() => {
    if (!data) return [];
    const set = new Set<string>();
    for (const c of data.done_cards) if (c.requester) set.add(c.requester);
    return Array.from(set).sort();
  }, [data]);

  const assigneeOptions = useMemo(() => {
    if (!data) return [];
    const m = new Map<string, string>();
    for (const c of data.done_cards) {
      if (c.assignee_id) m.set(c.assignee_id, c.assignee_name || c.assignee_id);
    }
    return Array.from(m.entries()).map(([value, label]) => ({ value, label }));
  }, [data]);

  const filteredDoneCards = useMemo(() => {
    if (!data) return [];
    return data.done_cards.filter((c) => {
      if (filterRequester && c.requester !== filterRequester) return false;
      if (filterAssignee && c.assignee_id !== filterAssignee) return false;
      return true;
    });
  }, [data, filterRequester, filterAssignee]);

  const exportPDF = () => {
    if (!data) return;
    const win = window.open("", "_blank");
    if (!win) return;
    const origin = window.location.origin;
    const dateLabel = (() => {
      try {
        return new Date(data.date + "T00:00:00").toLocaleDateString("es-ES", {
          weekday: "long",
          day: "2-digit",
          month: "long",
          year: "numeric",
        });
      } catch {
        return data.date;
      }
    })();
    const filterSub: string[] = [];
    if (filterRequester) filterSub.push(`solicitante=${filterRequester}`);
    if (filterAssignee) {
      const a = assigneeOptions.find((o) => o.value === filterAssignee);
      filterSub.push(`asignado=${a?.label || filterAssignee}`);
    }
    const escape = (s: string) =>
      s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
    const rows = filteredDoneCards
      .map((c) => {
        const tags = (c.tags || []).map(escape).join(", ");
        const link = `${origin}/?card=${c.id}`;
        return `<tr>
          <td class="num">${String(c.seq_num).padStart(5, "0")}</td>
          <td><a href="${link}">${escape(c.title)}</a></td>
          <td>${escape(c.requester || "")}</td>
          <td>${escape(c.assignee_name || "")}</td>
          <td>${escape(tags)}</td>
          <td class="num">${formatDuration(c.lead_time_ms)}</td>
        </tr>`;
      })
      .join("");
    const html = `<!doctype html>
<html lang="es"><head><meta charset="utf-8" />
<title>Reporte ${data.date}</title>
<style>
  @page { margin: 18mm 15mm; }
  body { font-family: system-ui, sans-serif; color: #222; }
  h1 { font-size: 18pt; margin-bottom: 4px; }
  .sub { color: #666; font-size: 10pt; margin-bottom: 16px; }
  .kpis { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; margin-bottom: 18px; }
  .kpi { border: 1px solid #ddd; border-radius: 6px; padding: 8px; }
  .kpi .l { font-size: 8pt; color: #888; text-transform: uppercase; }
  .kpi .v { font-size: 16pt; font-weight: 700; }
  table { width: 100%; border-collapse: collapse; font-size: 9pt; }
  th, td { border-bottom: 1px solid #e5e5e5; padding: 6px 4px; text-align: left; vertical-align: top; }
  th { background: #f5f5f5; font-weight: 600; }
  td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
  a { color: #1c7ed6; text-decoration: none; }
  a:hover { text-decoration: underline; }
  footer { margin-top: 20px; font-size: 8pt; color: #888; }
</style></head><body>
<h1>Reporte diario · ${escape(dateLabel)}</h1>
<div class="sub">${escape(data.date)} · ${escape(data.tz)}${
      filterSub.length ? " · filtros: " + filterSub.map(escape).join(", ") : ""
    }</div>
<div class="kpis">
  <div class="kpi"><div class="l">Hechas</div><div class="v">${filteredDoneCards.length}</div></div>
  <div class="kpi"><div class="l">Lead time avg</div><div class="v">${formatDuration(data.lead_time.avg_ms)}</div></div>
  <div class="kpi"><div class="l">Deadlines on-time</div><div class="v">${data.deadlines.met}/${data.deadlines.met + data.deadlines.missed}</div></div>
  <div class="kpi"><div class="l">Reabiertas</div><div class="v">${data.kpis.reopened}</div></div>
</div>
${summary?.summary ? `<p style="border-left:4px solid #1c7ed6; padding:8px 12px; background:#eef6fd; border-radius:4px;">${escape(summary.summary)}</p>` : ""}
<table>
  <thead><tr>
    <th class="num">#</th>
    <th>Titulo</th>
    <th>Solicitante</th>
    <th>Asignado</th>
    <th>Tags</th>
    <th class="num">Lead time</th>
  </tr></thead>
  <tbody>${rows || '<tr><td colspan="6" style="text-align:center;color:#888;">Sin tareas que cumplan el filtro.</td></tr>'}</tbody>
</table>
<footer>Generado por kanban · ${escape(origin)}</footer>
<script>window.addEventListener("load", () => setTimeout(() => window.print(), 250));</script>
</body></html>`;
    win.document.write(html);
    win.document.close();
  };

  if (err) {
    return (
      <Alert color="red" icon={<IconAlertTriangle size={14} />}>
        {err}
      </Alert>
    );
  }
  if (!data) {
    return (
      <Group justify="center" p="xl">
        <Loader size="sm" />
      </Group>
    );
  }

  const k = data.kpis;
  const onTimePct =
    k.deadlines_met + k.deadlines_missed > 0
      ? Math.round((k.deadlines_met / (k.deadlines_met + k.deadlines_missed)) * 100)
      : null;

  return (
    <Stack gap="md">
      <Group justify="space-between" wrap="wrap">
        <Group gap={6}>
          <IconCalendarStats size={20} />
          <Title order={4}>Reporte diario</Title>
        </Group>
        <Text size="sm" c="dimmed" tt="capitalize">
          {fmtDate(data.date)}
        </Text>
      </Group>

      <SimpleGrid cols={{ base: 2, sm: 4, md: 6 }} spacing="xs">
        <KPI label="Hechas" value={k.done} color="teal" icon={<IconCheck size={14} color="var(--mantine-color-teal-6)" />} />
        <KPI label="Creadas" value={k.created} icon={<IconPlus size={14} />} />
        <KPI label="Movimientos" value={k.moves} icon={<IconRefresh size={14} />} />
        <KPI
          label="Bloqueado"
          value={formatDuration(k.blocked_ms)}
          color="yellow"
          icon={<IconLock size={14} color="var(--mantine-color-yellow-6)" />}
        />
        <KPI
          label="Reabiertas"
          value={k.reopened}
          color={k.reopened > 0 ? "orange" : undefined}
          icon={<IconArrowBackUp size={14} />}
        />
        <KPI
          label="Deadlines"
          value={onTimePct != null ? `${onTimePct}%` : "—"}
          color={onTimePct == null ? "dimmed" : onTimePct >= 80 ? "teal" : "red"}
          sub={`${k.deadlines_met} on-time / ${k.deadlines_missed} vencidos`}
          icon={<IconHourglass size={14} />}
        />
      </SimpleGrid>

      <SimpleGrid cols={{ base: 1, sm: 2, md: 4 }} spacing="xs">
        <RankingList
          title="Asignado: mas hechas"
          rows={data.top_assignees_done}
          emptyText="Sin hechas con asignado."
          withAvatar
        />
        <RankingList
          title="Asignado: mas creadas"
          rows={data.top_assignees_created}
          emptyText="Sin actor en creadas."
          withAvatar
        />
        <RankingList
          title="Solicitante: mas atendidas"
          rows={data.top_requesters_done}
          emptyText="Sin solicitantes con hechas."
        />
        <RankingList
          title="Solicitante: mas aportadas"
          rows={data.top_requesters_added}
          emptyText="Sin nuevas con solicitante."
        />
      </SimpleGrid>

      {/* Bocadillo de agente — encima de tareas hechas */}
      <Paper
        withBorder
        radius="md"
        p="sm"
        bg="var(--mantine-color-blue-light)"
        style={{ borderLeftWidth: 4, borderLeftColor: "var(--mantine-color-blue-6)" }}
      >
        <Group justify="space-between" align="flex-start" wrap="nowrap">
          <Group gap={6} align="flex-start" wrap="nowrap" style={{ flex: 1, minWidth: 0 }}>
            <IconSparkles size={18} color="var(--mantine-color-blue-6)" style={{ flexShrink: 0, marginTop: 2 }} />
            <Box style={{ flex: 1 }}>
              {summaryErr && (
                <Alert color="red" mb={4} icon={<IconAlertTriangle size={14} />}>
                  {summaryErr}
                </Alert>
              )}
              {summaryLoading ? (
                <Group gap={6}><Loader size="xs" /><Text size="sm" c="dimmed">Generando resumen…</Text></Group>
              ) : summary?.summary ? (
                <>
                  <Text size="sm" style={{ whiteSpace: "pre-wrap" }}>{summary.summary}</Text>
                  {summary.generated_at && (
                    <Text size="xs" c="dimmed" mt={4}>
                      Generado {new Date(summary.generated_at).toLocaleString()} · {summary.model}
                    </Text>
                  )}
                </>
              ) : (
                <Text size="sm" c="dimmed" fs="italic">Aun no hay resumen del dia. Pulsa "Generar".</Text>
              )}
            </Box>
          </Group>
          <Group gap={4} wrap="nowrap">
            <Tooltip label={summary?.exists ? "Regenerar" : "Generar"} withArrow>
              <ActionIcon variant="subtle" color="blue" onClick={regenerateSummary} loading={summaryLoading} aria-label="Regenerar resumen">
                <IconRefresh size={16} />
              </ActionIcon>
            </Tooltip>
            <Tooltip label="Configurar prompt" withArrow>
              <ActionIcon variant="subtle" color="gray" onClick={openSettings} aria-label="Configurar prompt">
                <IconSettings size={16} />
              </ActionIcon>
            </Tooltip>
          </Group>
        </Group>
      </Paper>

      <MCard withBorder radius="md" p="sm">
        <Group justify="space-between" mb="xs" wrap="wrap" gap={6}>
          <Group gap={6} wrap="wrap">
            <Text fw={600} size="sm">
              Tareas hechas
            </Text>
            <Badge size="xs" variant="light">
              N {filteredDoneCards.length}
              {filteredDoneCards.length !== data.done_cards.length ? ` / ${data.done_cards.length}` : ""}
            </Badge>
            <Text size="xs" c="dimmed">
              Lead time avg {data.lead_time.samples > 0 ? formatDuration(data.lead_time.avg_ms) : "—"} · p50{" "}
              {data.lead_time.samples > 0 ? formatDuration(data.lead_time.p50_ms) : "—"} · p95{" "}
              {data.lead_time.samples > 0 ? formatDuration(data.lead_time.p95_ms) : "—"}
            </Text>
          </Group>
          <Group gap={6} wrap="nowrap">
            <Select
              size="xs"
              placeholder="Solicitante"
              data={requesterOptions}
              value={filterRequester}
              onChange={setFilterRequester}
              clearable
              searchable
              style={{ width: 160 }}
              aria-label="Filtrar por solicitante"
            />
            <Select
              size="xs"
              placeholder="Asignado"
              data={assigneeOptions}
              value={filterAssignee}
              onChange={setFilterAssignee}
              clearable
              searchable
              style={{ width: 160 }}
              aria-label="Filtrar por asignado"
            />
            <Button
              size="xs"
              leftSection={<IconDownload size={14} />}
              variant="light"
              onClick={exportPDF}
              data-test="daily-report-pdf"
            >
              PDF
            </Button>
          </Group>
        </Group>
        {filteredDoneCards.length === 0 ? (
          <Text size="xs" c="dimmed">
            Sin hechas en este dia.
          </Text>
        ) : (
          <ScrollArea style={{ maxHeight: 280 }} type="auto">
            <Table verticalSpacing={4} fz="xs" highlightOnHover striped="even">
              <Table.Thead>
                <Table.Tr>
                  <Table.Th style={{ width: 70 }}>#</Table.Th>
                  <Table.Th>Titulo</Table.Th>
                  <Table.Th>Solicitante</Table.Th>
                  <Table.Th>Asignado</Table.Th>
                  <Table.Th>Tags</Table.Th>
                  <Table.Th style={{ width: 110 }}>Lead time</Table.Th>
                </Table.Tr>
              </Table.Thead>
              <Table.Tbody>
                {filteredDoneCards.map((c) => (
                  <Table.Tr key={c.id}>
                    <Table.Td>
                      <Text size="xs" c="dimmed">
                        {String(c.seq_num).padStart(5, "0")}
                      </Text>
                    </Table.Td>
                    <Table.Td>
                      <UnstyledButton onClick={() => onJumpToCard?.(c.id)} style={{ textAlign: "left" }}>
                        <Text size="xs" fw={500} td="underline">
                          {c.title}
                        </Text>
                      </UnstyledButton>
                    </Table.Td>
                    <Table.Td>
                      <Text size="xs">{c.requester || "—"}</Text>
                    </Table.Td>
                    <Table.Td>
                      <Text size="xs">{c.assignee_name || "—"}</Text>
                    </Table.Td>
                    <Table.Td>
                      <Group gap={2} wrap="wrap">
                        {(c.tags || []).slice(0, 3).map((t) => (
                          <Badge key={t} size="xs" variant="light" color={tagColor(t)} radius="sm">
                            {t}
                          </Badge>
                        ))}
                      </Group>
                    </Table.Td>
                    <Table.Td>
                      <Text size="xs" c="dimmed">
                        {formatDuration(c.lead_time_ms)}
                      </Text>
                    </Table.Td>
                  </Table.Tr>
                ))}
              </Table.Tbody>
            </Table>
          </ScrollArea>
        )}
      </MCard>

      <SimpleGrid cols={{ base: 1, sm: 2 }} spacing="xs">
        <MCard withBorder radius="md" p="sm">
          <Group justify="space-between" mb={6}>
            <Text fw={600} size="sm">
              Movimientos por hora
            </Text>
            <Badge size="xs" variant="light">
              {k.moves}
            </Badge>
          </Group>
          {k.moves === 0 ? (
            <Text size="xs" c="dimmed">
              Sin movimientos.
            </Text>
          ) : (
            <BarChart
              h={160}
              data={hourlyChartData}
              dataKey="hora"
              series={[{ name: "movimientos", color: "blue.6" }]}
              tickLine="y"
              withTooltip
              valueFormatter={(v: number) => String(v)}
            />
          )}
        </MCard>

        <MCard withBorder radius="md" p="sm">
          <Text fw={600} size="sm" mb={6}>
            Tags trabajadas
          </Text>
          {data.tags_done.length === 0 ? (
            <Text size="xs" c="dimmed">
              Sin tags.
            </Text>
          ) : (
            <Group gap={4} wrap="wrap">
              {data.tags_done.map((t) => (
                <Badge key={t.name} variant="light" color={tagColor(t.name)} size="sm">
                  {t.name} · {t.count}
                </Badge>
              ))}
            </Group>
          )}
        </MCard>
      </SimpleGrid>

      {data.reopened_cards.length > 0 && (
        <MCard withBorder radius="md" p="sm">
          <Group gap={6} mb={6}>
            <IconArrowBackUp size={14} color="var(--mantine-color-orange-6)" />
            <Text fw={600} size="sm">
              Reabiertas (Done → otra)
            </Text>
            <Badge size="xs" variant="light" color="orange">
              {data.reopened_cards.length}
            </Badge>
          </Group>
          <Stack gap={4}>
            {data.reopened_cards.map((r) => (
              <Group key={r.card_id + r.ts} gap={6} wrap="nowrap" justify="space-between">
                <UnstyledButton onClick={() => onJumpToCard?.(r.card_id)} style={{ minWidth: 0, flex: 1 }}>
                  <Text size="xs" truncate td="underline">
                    {r.title}
                  </Text>
                </UnstyledButton>
                <Text size="xs" c="dimmed">
                  {r.from_column} → {r.to_column}
                </Text>
                {r.actor_name && (
                  <Badge size="xs" variant="light" color="cyan">
                    {r.actor_name}
                  </Badge>
                )}
              </Group>
            ))}
          </Stack>
        </MCard>
      )}

      {(data.deadlines.missed > 0 || data.deadlines.met > 0) && (
        <MCard withBorder radius="md" p="sm">
          <Group gap={6} mb={6}>
            <IconHourglass size={14} />
            <Text fw={600} size="sm">
              Deadlines
            </Text>
            <Badge size="xs" variant="light" color="teal">
              {data.deadlines.met} on-time
            </Badge>
            <Badge size="xs" variant="light" color="red">
              {data.deadlines.missed} vencidos
            </Badge>
          </Group>
          {data.deadlines.list.length > 0 && (
            <Stack gap={4}>
              {data.deadlines.list.map((d) => (
                <Group key={d.card_id} gap={6} justify="space-between" wrap="nowrap">
                  <UnstyledButton onClick={() => onJumpToCard?.(d.card_id)} style={{ minWidth: 0, flex: 1 }}>
                    <Text size="xs" truncate td="underline">
                      {d.title}
                    </Text>
                  </UnstyledButton>
                  <Text size="xs" c="red">
                    +{formatDuration(d.late_ms)} tarde
                  </Text>
                </Group>
              ))}
            </Stack>
          )}
        </MCard>
      )}

      <MCard withBorder radius="md" p="sm">
        <Group gap={6} mb={6}>
          <IconTrendingUp size={14} />
          <Text fw={600} size="sm">
            Cards estancadas (al final del dia)
          </Text>
          <Badge size="xs" variant="light" color="orange">
            {data.stale_cards.d7.length}d7
          </Badge>
          <Badge size="xs" variant="light" color="red">
            {data.stale_cards.d14.length}d14
          </Badge>
          <Badge size="xs" variant="filled" color="red">
            {data.stale_cards.d30.length}d30
          </Badge>
        </Group>
        <SimpleGrid cols={{ base: 1, sm: 3 }} spacing="xs">
          <Box>
            <Text size="xs" fw={500} c="orange" mb={4}>
              7-13 dias
            </Text>
            <Stack gap={2}>
              {data.stale_cards.d7.slice(0, 8).map((s) => (
                <UnstyledButton key={s.card_id} onClick={() => onJumpToCard?.(s.card_id)}>
                  <Text size="xs" truncate>
                    {s.title}{" "}
                    <Text span c="dimmed" size="xs">
                      · {s.column_name} · {s.days}d
                    </Text>
                  </Text>
                </UnstyledButton>
              ))}
              {data.stale_cards.d7.length === 0 && (
                <Text size="xs" c="dimmed">
                  Ninguna.
                </Text>
              )}
            </Stack>
          </Box>
          <Box>
            <Text size="xs" fw={500} c="red" mb={4}>
              14-29 dias
            </Text>
            <Stack gap={2}>
              {data.stale_cards.d14.slice(0, 8).map((s) => (
                <UnstyledButton key={s.card_id} onClick={() => onJumpToCard?.(s.card_id)}>
                  <Text size="xs" truncate>
                    {s.title}{" "}
                    <Text span c="dimmed" size="xs">
                      · {s.column_name} · {s.days}d
                    </Text>
                  </Text>
                </UnstyledButton>
              ))}
              {data.stale_cards.d14.length === 0 && (
                <Text size="xs" c="dimmed">
                  Ninguna.
                </Text>
              )}
            </Stack>
          </Box>
          <Box>
            <Text size="xs" fw={500} c="red.8" mb={4}>
              30+ dias
            </Text>
            <Stack gap={2}>
              {data.stale_cards.d30.slice(0, 8).map((s) => (
                <UnstyledButton key={s.card_id} onClick={() => onJumpToCard?.(s.card_id)}>
                  <Text size="xs" truncate fw={600}>
                    {s.title}{" "}
                    <Text span c="dimmed" size="xs" fw={400}>
                      · {s.column_name} · {s.days}d
                    </Text>
                  </Text>
                </UnstyledButton>
              ))}
              {data.stale_cards.d30.length === 0 && (
                <Text size="xs" c="dimmed">
                  Ninguna.
                </Text>
              )}
            </Stack>
          </Box>
        </SimpleGrid>
      </MCard>

      <Divider />
      <Group gap={6} justify="space-between">
        <Group gap={4}>
          <IconClock size={14} />
          <Text size="xs" c="dimmed">
            TZ: {data.tz} · cards archivadas hoy: {data.archived_today}
          </Text>
        </Group>
      </Group>

      <Modal opened={settingsOpen} onClose={() => setSettingsOpen(false)} title="Prompt del agente diario" size="lg" zIndex={500}>
        <Stack gap="sm">
          <Text size="xs" c="dimmed">
            Plantilla que el agente recibe junto al JSON del reporte. Compartida por todos los usuarios.
          </Text>
          <Textarea
            autosize
            minRows={6}
            maxRows={20}
            value={promptDraft}
            onChange={(e) => setPromptDraft(e.currentTarget.value)}
            data-test="daily-report-prompt"
          />
          <Group justify="space-between">
            <Button size="xs" variant="subtle" onClick={resetSettings}>
              Restablecer por defecto
            </Button>
            <Group gap={6}>
              <Button size="xs" variant="subtle" color="gray" onClick={() => setSettingsOpen(false)}>
                Cancelar
              </Button>
              <Button size="xs" onClick={saveSettings} data-test="daily-report-prompt-save">
                Guardar
              </Button>
            </Group>
          </Group>
        </Stack>
      </Modal>
    </Stack>
  );
}
