import Link from "next/link";
import { Card } from "@/components/ui/card";
import { StatusBadge } from "@/components/ui/status-badge";
import { cn } from "@/lib/cn";
import { DEFAULT_QUERY, dashboardHref } from "@/lib/dashboard-query";
import { formatDate, formatDateTime, formatDuration, formatElapsed, formatPercent, formatResponseTime } from "@/lib/format";
import { STATUS_LABELS, type DisplayStatus } from "@/lib/status";
import type { AttentionRow, DashboardSummary, ResponseTimeStats, TrendDay } from "@/server/dashboard/service";

// Status colours (Tailwind emerald/amber/red 500; slate via variables so they follow the theme) — reserved for state,
// always paired with a label so colour is never the only signal.
const FILL = { WORKING: "#10b981", SLOW: "#f59e0b", NOT_WORKING: "#ef4444", PENDING: "var(--color-slate-300)", DISABLED: "var(--color-slate-400)" } as const;

// ---------------------------------------------------------------------------
// Health overview: how the monitored apps split by status right now
// ---------------------------------------------------------------------------

export function HealthOverview({ summary }: { summary: DashboardSummary }) {
  const monitored = summary.total - summary.disabled;
  const responding = summary.working + summary.slow;
  const checked = responding + summary.notWorking;
  const segments: Array<{ status: DisplayStatus; value: number }> = [
    { status: "WORKING", value: summary.working },
    { status: "SLOW", value: summary.slow },
    { status: "NOT_WORKING", value: summary.notWorking },
    { status: "PENDING", value: summary.pending },
    { status: "DISABLED", value: summary.disabled },
  ];

  return (
    <Card className="p-4">
      <h2 className="text-sm font-semibold text-slate-900">Health overview</h2>
      <p className="mt-2 text-3xl font-semibold tabular-nums text-slate-900">
        {checked === 0 ? "—" : formatPercent((responding / checked) * 100)}
      </p>
      <p className="text-xs text-slate-500">
        {checked === 0
          ? "No checked apps yet"
          : `${responding} of ${checked} checked apps responding · ${monitored} monitored`}
      </p>

      <div className="mt-4 flex h-3 w-full gap-0.5 overflow-hidden rounded-sm" role="img" aria-label={overviewAlt(segments)}>
        {segments
          .filter((segment) => segment.value > 0)
          .map((segment) => (
            <span
              key={segment.status}
              title={`${STATUS_LABELS[segment.status]}: ${segment.value}`}
              style={{ flexGrow: segment.value, backgroundColor: FILL[segment.status] }}
              className="block min-w-1 rounded-[1px]"
            />
          ))}
        {summary.total === 0 && <span className="block flex-1 bg-slate-100" />}
      </div>

      <ul className="mt-3 grid grid-cols-2 gap-x-4 gap-y-1.5 text-xs">
        {segments.map((segment) => (
          <li key={segment.status} className="flex items-center justify-between gap-2">
            <Link
              href={dashboardHref(DEFAULT_QUERY, { status: segment.status })}
              className="flex min-w-0 items-center gap-1.5 text-slate-600 hover:text-slate-900"
            >
              <span className="size-2 shrink-0 rounded-full" style={{ backgroundColor: FILL[segment.status] }} aria-hidden />
              <span className="truncate">{STATUS_LABELS[segment.status]}</span>
            </Link>
            <span className="font-medium tabular-nums text-slate-900">{segment.value}</span>
          </li>
        ))}
      </ul>
    </Card>
  );
}

function overviewAlt(segments: Array<{ status: DisplayStatus; value: number }>): string {
  return segments.map((s) => `${STATUS_LABELS[s.status]} ${s.value}`).join(", ");
}

// ---------------------------------------------------------------------------
// Check outcomes per day (stacked columns)
// ---------------------------------------------------------------------------

const CHART = { width: 420, height: 130, top: 8, bottom: 22, left: 28, right: 4, bar: 18, gap: 2, radius: 3 };

function niceMax(value: number): number {
  if (value <= 5) return 5;
  const magnitude = 10 ** Math.floor(Math.log10(value));
  const step = magnitude >= 10 ? magnitude / 2 : magnitude;
  return Math.ceil(value / step) * step;
}

/** Rectangle with rounded top corners only: the data end is rounded, the baseline stays square. */
function topRoundedPath(x: number, y: number, w: number, h: number, r: number): string {
  const radius = Math.min(r, h, w / 2);
  return `M${x},${y + h} V${y + radius} Q${x},${y} ${x + radius},${y} H${x + w - radius} Q${x + w},${y} ${x + w},${y + radius} V${y + h} Z`;
}

export function CheckTrendChart({ days, timezone }: { days: TrendDay[]; timezone: string }) {
  const totals = days.map((d) => d.working + d.slow + d.notWorking);
  const max = niceMax(Math.max(...totals, 0));
  const plotHeight = CHART.height - CHART.top - CHART.bottom;
  const plotWidth = CHART.width - CHART.left - CHART.right;
  const band = plotWidth / days.length;
  const scale = (value: number) => (value / max) * plotHeight;
  const baseline = CHART.top + plotHeight;
  const hasData = totals.some((t) => t > 0);

  const label = (day: string) => {
    const [y, m, d] = day.split("-").map(Number);
    return formatDate(new Date(Date.UTC(y, m - 1, d, 12)), "UTC").slice(0, 6); // "18 Sep"
  };
  const daySummary = (d: TrendDay) =>
    `${label(d.day)}: ${d.working} working, ${d.slow} slow, ${d.notWorking} not working`;

  return (
    <Card className="p-4">
      <div className="flex items-baseline justify-between gap-2">
        <h2 className="text-sm font-semibold text-slate-900">Check outcomes</h2>
        <span className="text-xs text-slate-500">last {days.length} days · {timezone.replaceAll("_", " ")}</span>
      </div>

      {hasData ? (
        <svg
          viewBox={`0 0 ${CHART.width} ${CHART.height}`}
          className="mt-2 h-auto w-full"
          role="img"
          aria-label={`Health checks per day for the last ${days.length} days, split by outcome`}
        >
          {[0, 0.5, 1].map((fraction) => {
            const y = baseline - fraction * plotHeight;
            return (
              <g key={fraction}>
                <line x1={CHART.left} x2={CHART.width - CHART.right} y1={y} y2={y} strokeWidth={1} className="stroke-slate-200" />
                <text x={CHART.left - 6} y={y + 3} textAnchor="end" fontSize={9} className="fill-slate-500">
                  {Math.round(fraction * max)}
                </text>
              </g>
            );
          })}

          {days.map((d, i) => {
            const x = CHART.left + i * band + (band - CHART.bar) / 2;
            const stack = [
              { status: "WORKING" as const, value: d.working },
              { status: "SLOW" as const, value: d.slow },
              { status: "NOT_WORKING" as const, value: d.notWorking },
            ].filter((s) => s.value > 0);
            let y = baseline;
            return (
              <g key={d.day}>
                <title>{daySummary(d)}</title>
                {stack.map((segment, index) => {
                  const h = scale(segment.value);
                  const isTop = index === stack.length - 1;
                  // A 2px surface gap separates touching segments.
                  const gap = index === 0 ? 0 : CHART.gap;
                  const top = y - h;
                  const drawnTop = top + gap;
                  const drawnHeight = Math.max(0, h - gap);
                  y = top;
                  return isTop ? (
                    <path key={segment.status} d={topRoundedPath(x, drawnTop, CHART.bar, drawnHeight, CHART.radius)} fill={FILL[segment.status]} />
                  ) : (
                    <rect key={segment.status} x={x} y={drawnTop} width={CHART.bar} height={drawnHeight} fill={FILL[segment.status]} />
                  );
                })}
                {/* Hover target covers the whole band so the tooltip is easy to hit. */}
                <rect x={CHART.left + i * band} y={CHART.top} width={band} height={plotHeight} fill="transparent" />
                {(i % 2 === days.length % 2 || days.length <= 7) && (
                  <text x={x + CHART.bar / 2} y={CHART.height - 8} textAnchor="middle" fontSize={9} className="fill-slate-500">
                    {label(d.day)}
                  </text>
                )}
              </g>
            );
          })}
        </svg>
      ) : (
        <p className="mt-6 mb-4 text-center text-sm text-slate-500">No health checks in the last {days.length} days.</p>
      )}

      <ul className="mt-1 flex flex-wrap gap-x-4 gap-y-1 text-xs text-slate-600" aria-label="Legend">
        {(["WORKING", "SLOW", "NOT_WORKING"] as const).map((status) => (
          <li key={status} className="flex items-center gap-1.5">
            <span className="size-2 rounded-full" style={{ backgroundColor: FILL[status] }} aria-hidden />
            {STATUS_LABELS[status]}
          </li>
        ))}
      </ul>

      {/* Table view of the same data for screen readers and copy/paste. */}
      <table className="sr-only">
        <caption>Health checks per day</caption>
        <thead>
          <tr>
            <th>Day</th>
            <th>Working</th>
            <th>Slow</th>
            <th>Not Working</th>
          </tr>
        </thead>
        <tbody>
          {days.map((d) => (
            <tr key={d.day}>
              <td>{d.day}</td>
              <td>{d.working}</td>
              <td>{d.slow}</td>
              <td>{d.notWorking}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </Card>
  );
}

// ---------------------------------------------------------------------------
// Response times of the apps that are responding right now
// ---------------------------------------------------------------------------

export function ResponseTimeCard({ stats, slowThresholdSeconds }: { stats: ResponseTimeStats; slowThresholdSeconds: number }) {
  const buckets = [
    { label: "under 1 s", value: stats.buckets.under1s, fill: "#10b981" },
    { label: "1–5 s", value: stats.buckets.under5s, fill: "#34d399" },
    { label: "5–15 s", value: stats.buckets.under15s, fill: "#fbbf24" },
    { label: "15 s or more", value: stats.buckets.over15s, fill: "#f59e0b" },
  ];
  const maxBucket = Math.max(...buckets.map((b) => b.value), 1);

  return (
    <Card className="p-4">
      <div className="flex items-baseline justify-between gap-2">
        <h2 className="text-sm font-semibold text-slate-900">Response times</h2>
        <span className="text-xs text-slate-500">slow above {slowThresholdSeconds} s</span>
      </div>

      <dl className="mt-2 grid grid-cols-3 gap-3">
        <Figure label="Average" value={stats.averageMs} />
        <Figure label="Median" value={stats.medianMs} />
        <Figure label="Slowest" value={stats.slowestMs} emphasis={stats.slowestMs !== null && stats.slowestMs > slowThresholdSeconds * 1000} />
      </dl>

      {stats.measured === 0 ? (
        <p className="mt-4 text-xs text-slate-500">No responding apps to measure yet.</p>
      ) : (
        <ul className="mt-4 space-y-1.5" aria-label="Apps by latest response time">
          {buckets.map((bucket) => (
            <li key={bucket.label} className="grid grid-cols-[5.5rem_1fr_1.5rem] items-center gap-2 text-xs">
              <span className="text-slate-600">{bucket.label}</span>
              <span className="h-2.5 rounded-sm bg-slate-100">
                <span
                  className="block h-full rounded-sm"
                  style={{ width: `${(bucket.value / maxBucket) * 100}%`, backgroundColor: bucket.value ? bucket.fill : "transparent" }}
                  title={`${bucket.value} app${bucket.value === 1 ? "" : "s"} ${bucket.label}`}
                />
              </span>
              <span className="text-right font-medium tabular-nums text-slate-900">{bucket.value}</span>
            </li>
          ))}
        </ul>
      )}
    </Card>
  );
}

function Figure({ label, value, emphasis }: { label: string; value: number | null; emphasis?: boolean }) {
  return (
    <div>
      <dt className="text-xs text-slate-500">{label}</dt>
      <dd className={cn("text-lg font-semibold tabular-nums", emphasis ? "text-amber-700" : "text-slate-900")}>
        {value === null ? <span className="text-slate-300">—</span> : formatDuration(value)}
      </dd>
    </div>
  );
}

// ---------------------------------------------------------------------------
// Attention lists
// ---------------------------------------------------------------------------

export function AttentionList({
  title,
  rows,
  empty,
  detail,
  tz,
  seeAll,
}: {
  title: string;
  rows: AttentionRow[];
  empty: string;
  detail: (row: AttentionRow) => React.ReactNode;
  tz: string;
  seeAll?: string;
}) {
  return (
    <Card className="overflow-hidden">
      <div className="flex items-center justify-between gap-2 border-b border-slate-200 px-4 py-2.5">
        <h2 className="text-sm font-semibold text-slate-900">{title}</h2>
        {seeAll && rows.length > 0 && (
          <Link href={seeAll} className="text-xs font-medium text-slate-600 hover:text-slate-900">
            See all
          </Link>
        )}
      </div>
      {rows.length === 0 ? (
        <p className="px-4 py-6 text-center text-sm text-slate-500">{empty}</p>
      ) : (
        <ul className="divide-y divide-slate-100">
          {rows.map((row) => (
            <li key={row.id} className="flex items-center gap-3 px-4 py-2.5">
              <span className="w-14 shrink-0 font-mono text-xs font-medium text-slate-900">{row.projectCode}</span>
              <Link
                href={`/apps/${row.id}`}
                className="min-w-0 flex-1 truncate text-sm font-medium text-slate-900 underline-offset-2 hover:underline"
                title={row.lastCheckedAt ? `Checked ${formatDateTime(row.lastCheckedAt, tz)}` : undefined}
              >
                {row.projectName}
              </Link>
              <StatusBadge status={row.currentStatus} className="hidden sm:inline-flex" />
              <span className="shrink-0 text-right text-xs tabular-nums text-slate-600">{detail(row)}</span>
            </li>
          ))}
        </ul>
      )}
    </Card>
  );
}

export function outageDetail(row: AttentionRow) {
  return row.failureStartedAt ? (
    <span className="text-red-700" title={row.currentErrorMessage ?? undefined}>
      down {formatElapsed(row.failureStartedAt)}
    </span>
  ) : (
    <span className="text-red-700">down</span>
  );
}

export function slowDetail(row: AttentionRow) {
  return row.currentResponseTimeMs !== null ? (
    <span className={row.currentStatus === "SLOW" ? "font-medium text-amber-700" : undefined}>
      {formatResponseTime(row.currentResponseTimeMs)}
    </span>
  ) : null;
}
