"use client";

import { useMemo, useState } from "react";
import { Activity, LayoutGrid } from "lucide-react";
import type { ForgejoHeatmapDay, ForgejoLastPush } from "@/lib/api-forgejo";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
} from "@/components/ui/select";

interface ForgejoHeatmapProps {
  days: ForgejoHeatmapDay[];
  maxCount: number;
  totalAdditions?: number;
  totalDeletions?: number;
  hasLineStats?: boolean;
  lastPush?: ForgejoLastPush | null;
  isLoading?: boolean;
}

// ── Line chart layout ──────────────────────────────────────────────────────
const LVW  = 580;
const LVH  = 188;
const LP_L = 34;  // Y-axis labels
const LP_R = 8;
const LP_T = 10;
const LP_B = 24;
const LCW  = LVW - LP_L - LP_R;
const LCH  = LVH - LP_T - LP_B;

// ── Heatmap layout ─────────────────────────────────────────────────────────
const HLEFT   = 32;
const HRIGHT  = 14;
const HVH     = LVH;

// ── Types & data ───────────────────────────────────────────────────────────
type RangeKey = "7d" | "14d" | "30d" | "90d" | "6m" | "1y";
type ActivityDatum = {
  date: string;
  count: number;
  label: string;
};

const RANGE_DAYS: Record<RangeKey, number> = {
  "7d": 7, "14d": 14, "30d": 30, "90d": 90, "6m": 183, "1y": 365,
};
const RANGE_SUMMARY: Record<RangeKey, string> = {
  "7d": "7 days",
  "14d": "14 days",
  "30d": "30 days",
  "90d": "90 days",
  "6m": "6 months",
  "1y": "1 year",
};
const RANGE_LABELS: RangeKey[] = ["7d", "14d", "30d", "90d", "6m", "1y"];
const MONTHS = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];

// ── Colors ─────────────────────────────────────────────────────────────────
const VIOLET  = "rgba(139,92,246,1)";
const GREEN   = "rgba(52,211,153,1)";
const RED     = "rgba(248,113,113,1)";
const W70     = "rgba(255,255,255,0.70)";
const W40     = "rgba(255,255,255,0.40)";
const W12     = "rgba(255,255,255,0.12)";

// Green palette for heatmap cells
const HMAP_FILL = [
  "rgba(16,185,129,0.08)",   // 0 — empty
  "rgba(16,185,129,0.28)",   // 1
  "rgba(16,185,129,0.55)",   // 2
  "rgba(16,185,129,0.78)",   // 3
  "rgba(16,185,129,1.00)",   // 4 — full emerald
];

// ── Helpers ────────────────────────────────────────────────────────────────
function isoDate(d: Date): string {
  return [d.getFullYear(), String(d.getMonth()+1).padStart(2,"0"), String(d.getDate()).padStart(2,"0")].join("-");
}
function dateFromIsoDate(value: string): Date {
  const [year, month, day] = value.split("-").map(Number);
  return new Date(year, month - 1, day);
}
function fmtLines(n: number): string {
  if (n >= 1_000_000) return `${(n/1_000_000).toFixed(1)}M`;
  if (n >= 1_000)     return `${(n/1_000).toFixed(1)}k`;
  return n.toLocaleString();
}
function fmtRelative(iso: string): string {
  const s = Math.floor((Date.now() - new Date(iso).getTime()) / 1000);
  if (s < 60)  return "just now";
  const m = Math.floor(s/60); if (m < 60) return `${m}m ago`;
  const h = Math.floor(m/60); if (h < 24) return `${h}h ago`;
  return `${Math.floor(h/24)}d ago`;
}
function fmtActivityDate(date: string): string {
  const d = dateFromIsoDate(date);
  return `${MONTHS[d.getMonth()]} ${d.getDate()}`;
}

// Catmull-Rom → cubic bezier smooth path
function smoothPath(pts: {x:number;y:number}[]): string {
  if (pts.length < 2) return "";
  const t = 1/6;
  let d = `M${pts[0].x.toFixed(1)},${pts[0].y.toFixed(1)}`;
  for (let i = 0; i < pts.length-1; i++) {
    const p0=pts[Math.max(0,i-1)], p1=pts[i], p2=pts[i+1], p3=pts[Math.min(pts.length-1,i+2)];
    d += ` C${(p1.x+t*(p2.x-p0.x)).toFixed(1)},${(p1.y+t*(p2.y-p0.y)).toFixed(1)} ${(p2.x-t*(p3.x-p1.x)).toFixed(1)},${(p2.y-t*(p3.y-p1.y)).toFixed(1)} ${p2.x.toFixed(1)},${p2.y.toFixed(1)}`;
  }
  return d;
}

function heatFill(count: number, maxCount: number): string {
  if (count <= 0 || maxCount <= 0) return HMAP_FILL[0];
  const intensity = Math.max(0.18, Math.min(1, count / maxCount));
  const alpha = 0.18 + intensity * 0.82;
  return `rgba(16,185,129,${alpha.toFixed(2)})`;
}

function RangeSelect({
  value,
  onValueChange,
  accent,
  ariaLabel,
}: {
  value: RangeKey;
  onValueChange: (value: RangeKey) => void;
  accent: "violet" | "green";
  ariaLabel: string;
}) {
  const isViolet = accent === "violet";
  return (
    <Select value={value} onValueChange={(next) => onValueChange(next as RangeKey)}>
      <SelectTrigger
        aria-label={ariaLabel}
        className="h-8 w-[86px] rounded-lg px-2.5 text-xs font-semibold shadow-none focus:ring-offset-0"
        style={
          isViolet
            ? {
                background: "rgba(139,92,246,0.12)",
                borderColor: "rgba(139,92,246,0.34)",
                color: VIOLET,
              }
            : {
                background: "rgba(16,185,129,0.12)",
                borderColor: "rgba(16,185,129,0.34)",
                color: GREEN,
              }
        }
      >
        <span>{value}</span>
      </SelectTrigger>
      <SelectContent align="end" className="min-w-[8rem]">
        {RANGE_LABELS.map((range) => (
          <SelectItem key={range} value={range}>
            {RANGE_SUMMARY[range]}
          </SelectItem>
        ))}
      </SelectContent>
    </Select>
  );
}

// ── Line chart sub-component ───────────────────────────────────────────────
function LineChart({ days, range, onRangeChange, lastPush }: {
  days: ForgejoHeatmapDay[];
  range: RangeKey;
  onRangeChange: (r: RangeKey) => void;
  lastPush: ForgejoLastPush | null;
}) {
  const [hoveredPoint, setHoveredPoint] = useState<ActivityDatum | null>(null);
  const { points, xLabels, yLabels, linePath, areaPath, totalEvents } = useMemo(() => {
    const lookup = new Map(days.map(d => [d.date, d.count]));
    const numDays = RANGE_DAYS[range];
    const today = new Date(); today.setHours(0,0,0,0);
    const daily = Array.from({length: numDays}, (_, i) => {
      const d = new Date(today); d.setDate(d.getDate()-(numDays-1-i));
      const key = isoDate(d);
      return { date: key, count: lookup.get(key) ?? 0 };
    });

    const useWeekly = numDays > 30;
    const series = useWeekly
      ? Array.from({length: Math.ceil(daily.length/7)}, (_, i) => {
          const chunk = daily.slice(i*7, i*7+7);
          return {
            date: chunk[0].date,
            count: chunk.reduce((s,d) => s+d.count, 0),
            label: `Week of ${fmtActivityDate(chunk[0].date)}`,
          };
        })
      : daily.map(d => ({
          ...d,
          label: fmtActivityDate(d.date),
        }));

    const localMax = Math.max(...series.map(p => p.count), 1);
    const pts = series.map((p, i) => ({
      x: LP_L + (i / Math.max(series.length-1,1)) * LCW,
      y: LP_T + LCH - (p.count / localMax) * LCH,
      date: p.date, count: p.count, label: p.label,
    }));
    const totalEvents = daily.reduce((s, d) => s + d.count, 0);

    // X labels
    const step = Math.max(1, Math.floor(series.length / 5));
    const xLabels = series
      .filter((_, i) => i % step === 0 || i === series.length-1)
      .map((p) => {
        const idx = series.indexOf(p);
        const d = dateFromIsoDate(p.date);
        return {
          x: LP_L + (idx / Math.max(series.length-1,1)) * LCW,
          label: useWeekly || numDays > 7
            ? `${MONTHS[d.getMonth()]} ${d.getDate()}`
            : ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"][d.getDay()],
        };
      });

    // Y labels
    const yStep = localMax / 4;
    const yLabels = [1,2,3,4].map(i => {
      const val = Math.round(yStep * i);
      return { y: LP_T + LCH - (val/localMax)*LCH, label: val >= 1000 ? `${(val/1000).toFixed(0)}k` : String(val) };
    });

    const linePath = smoothPath(pts);
    const last = pts[pts.length-1], first = pts[0];
    const baseline = LP_T + LCH;
    const areaPath = pts.length < 2 ? "" :
      linePath + ` L${last.x.toFixed(1)},${baseline} L${first.x.toFixed(1)},${baseline} Z`;

    return { points: pts, xLabels, yLabels, linePath, areaPath, totalEvents };
  }, [days, range]);

  const baseline = LP_T + LCH;
  const lastPt = points[points.length-1];
  const activePoint = hoveredPoint
    ? points.find((p) => p.date === hoveredPoint.date) ?? null
    : null;
  const displayedCount = hoveredPoint?.count ?? totalEvents;
  const displayedLabel = hoveredPoint?.label ?? RANGE_SUMMARY[range];

  return (
    <div>
      {/* Header */}
      <div className="mb-3 flex items-center justify-between">
        <div className="flex items-center gap-2">
          <Activity className="h-4 w-4" style={{ color: VIOLET }} />
          <span className="text-sm font-semibold text-strong">Git Activity</span>
          <span className="ml-1 rounded px-1.5 py-0.5 text-[11px] font-semibold tabular-nums"
            style={{background:"rgba(139,92,246,0.14)", color:VIOLET}}>
            {displayedCount.toLocaleString()}
          </span>
          <span className="hidden text-xs sm:inline" style={{color:W40}}>
            {hoveredPoint ? `on ${displayedLabel}` : `in ${displayedLabel}`}
          </span>
        </div>
        <RangeSelect
          value={range}
          onValueChange={onRangeChange}
          accent="violet"
          ariaLabel="Select Git Activity range"
        />
      </div>

      {/* SVG */}
      <svg viewBox={`0 0 ${LVW} ${LVH}`} width="100%" style={{ display:"block", height:LVH, overflow:"visible" }}
           aria-label={`Git activity last ${range}`}>
        <defs>
          <linearGradient id="la-fill" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%"   stopColor="rgba(139,92,246,0.50)" />
            <stop offset="80%"  stopColor="rgba(139,92,246,0.05)" />
            <stop offset="100%" stopColor="rgba(139,92,246,0.00)" />
          </linearGradient>
          <clipPath id="la-clip"><rect x={LP_L} y={LP_T-2} width={LCW} height={LCH+4}/></clipPath>
        </defs>
        {yLabels.map(({y, label}) => (
          <g key={label}>
            <line x1={LP_L} y1={y} x2={LP_L+LCW} y2={y} strokeDasharray="4 5" style={{stroke:W12,strokeWidth:1}}/>
            <text x={LP_L-5} y={y+3.5} fontSize={9} textAnchor="end" style={{fill:W40}}>{label}</text>
          </g>
        ))}
        <line x1={LP_L} y1={baseline} x2={LP_L+LCW} y2={baseline} style={{stroke:W12,strokeWidth:1}}/>
        <path d={areaPath} style={{fill:"url(#la-fill)"}} clipPath="url(#la-clip)"/>
        <path d={linePath} style={{fill:"none",stroke:VIOLET,strokeWidth:2,strokeLinejoin:"round",strokeLinecap:"round"}} clipPath="url(#la-clip)"/>
        {activePoint && (
          <>
            <line x1={activePoint.x} y1={LP_T} x2={activePoint.x} y2={baseline} strokeDasharray="3 4" style={{stroke:"rgba(139,92,246,0.55)",strokeWidth:1}}/>
            <circle cx={activePoint.x} cy={activePoint.y} r={7} style={{fill:"rgba(139,92,246,0.18)"}}/>
            <circle cx={activePoint.x} cy={activePoint.y} r={4} style={{fill:VIOLET}}/>
          </>
        )}
        {lastPt && (
          <>
            <circle cx={lastPt.x} cy={lastPt.y} r={5.5} style={{fill:"rgba(10,10,30,0.9)"}}/>
            <circle cx={lastPt.x} cy={lastPt.y} r={3.5} style={{fill:VIOLET}}/>
          </>
        )}
        {xLabels.map(({x, label}, i) => (
          <text key={i} x={x} y={LVH-6} fontSize={10} textAnchor="middle" style={{fill:W70}}>{label}</text>
        ))}
        {points.map((p,i) => (
          <rect
            key={i}
            x={p.x - LCW/Math.max(points.length,1)/2}
            y={LP_T}
            width={LCW/Math.max(points.length,1)}
            height={LCH}
            tabIndex={0}
            onMouseEnter={() => setHoveredPoint({ date: p.date, count: p.count, label: p.label })}
            onFocus={() => setHoveredPoint({ date: p.date, count: p.count, label: p.label })}
            onMouseLeave={() => setHoveredPoint(null)}
            onBlur={() => setHoveredPoint(null)}
            style={{fill:"transparent", outline:"none"}}
          >
            <title>{p.date}{p.count > 0 ? `: ${p.count} event${p.count!==1?"s":""}` : ": no activity"}</title>
          </rect>
        ))}
      </svg>

      {/* Last push — centered at bottom of card */}
      {lastPush && (
        <div className="mt-3 flex items-start justify-center gap-2.5">
          <span className="mt-0.5 shrink-0 rounded px-1.5 py-0.5 font-mono text-[11px] font-semibold"
            style={{background:"rgba(139,92,246,0.20)", color:VIOLET}}>
            {lastPush.sha}
          </span>
          <div className="min-w-0">
            <p className="truncate text-sm font-medium" style={{color:"rgba(255,255,255,0.90)"}}>
              {lastPush.message}
            </p>
            <p className="mt-0.5 text-center text-xs" style={{color:"rgba(255,255,255,0.50)"}}>
              {lastPush.author} pushed to{" "}
              <span style={{color:"rgba(139,92,246,0.90)"}}>{lastPush.branch}</span>
              {" · "}{lastPush.repo}{" · "}{fmtRelative(lastPush.pushed_at)}
            </p>
          </div>
        </div>
      )}
    </div>
  );
}

// ── Heatmap grid sub-component ─────────────────────────────────────────────
function HeatmapGrid({ days, range, onRangeChange }: {
  days: ForgejoHeatmapDay[];
  range: RangeKey;
  onRangeChange: (r: RangeKey) => void;
}) {
  const [hoveredDay, setHoveredDay] = useState<ActivityDatum | null>(null);
  const heatmap = useMemo(() => {
    const lookup = new Map(days.map(d => [d.date, d.count]));
    const today = new Date(); today.setHours(0,0,0,0);
    const numDays = RANGE_DAYS[range];
    const rangeStart = new Date(today);
    rangeStart.setDate(rangeStart.getDate() - (numDays - 1));
    const daily = Array.from({ length: numDays }, (_, i) => {
      const d = new Date(rangeStart);
      d.setDate(d.getDate() + i);
      const date = isoDate(d);
      return {
        date,
        count: lookup.get(date) ?? 0,
        label:
          numDays <= 7
            ? ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"][d.getDay()]
            : `${MONTHS[d.getMonth()]} ${d.getDate()}`,
      };
    });

    const totalEvents = daily.reduce((s, d) => s + d.count, 0);
    const maxCount = Math.max(...daily.map(d => d.count), 0);

    if (numDays <= 30) {
      const gap = numDays <= 7 ? 12 : 4;
      const maxCell = numDays <= 7 ? 42 : 15;
      const availableWidth = LVW - HLEFT - HRIGHT;
      const cell = Math.max(
        8,
        Math.min(maxCell, Math.floor((availableWidth - gap * (numDays - 1)) / numDays)),
      );
      const width = daily.length * cell + Math.max(0, daily.length - 1) * gap;
      const x = HLEFT + (availableWidth - width) / 2;
      const y = numDays <= 7 ? 48 : 54;

      return {
        mode: "strip" as const,
        daily,
        totalEvents,
        maxCount,
        cell,
        gap,
        x,
        y,
      };
    }

    const calendarStart = new Date(rangeStart);
    calendarStart.setDate(calendarStart.getDate() - calendarStart.getDay());
    const calendarEnd = new Date(today);
    calendarEnd.setDate(calendarEnd.getDate() + (6 - calendarEnd.getDay()));
    const weekCount = Math.max(
      1,
      Math.ceil(((calendarEnd.getTime() - calendarStart.getTime()) / 86400000 + 1) / 7),
    );

    type CalendarCell = { date:string; count:number; label:string; outsideRange:boolean };
    const builtWeeks: CalendarCell[][] = [];
    const monthLabelList: {weekIdx:number; label:string}[] = [];
    let lastMonth = -1;
    const cur = new Date(calendarStart);

    for (let w = 0; w < weekCount; w++) {
      const week: CalendarCell[] = [];
      for (let d = 0; d < 7; d++) {
        const dateStr = isoDate(cur);
        const month = cur.getMonth();
        if (cur >= rangeStart && cur <= today && month !== lastMonth) {
          monthLabelList.push({ weekIdx: w, label: MONTHS[month] });
          lastMonth = month;
        }
        week.push({
          date: dateStr,
          count: lookup.get(dateStr) ?? 0,
          label: fmtActivityDate(dateStr),
          outsideRange: cur < rangeStart || cur > today,
        });
        cur.setDate(cur.getDate()+1);
      }
      builtWeeks.push(week);
    }

    const gap = 3;
    const availableWidth = LVW - HLEFT - HRIGHT;
    const maxCell = range === "90d" ? 14 : range === "6m" ? 12 : 8;
    const cell = Math.max(
      6,
      Math.min(maxCell, Math.floor((availableWidth - gap * (weekCount - 1)) / weekCount)),
    );
    const stride = cell + gap;
    const width = weekCount * cell + Math.max(0, weekCount - 1) * gap;
    const x = HLEFT + (availableWidth - width) / 2;
    const y = 28;

    return {
      mode: "calendar" as const,
      weeks: builtWeeks,
      monthLabels: monthLabelList,
      totalEvents,
      maxCount,
      cell,
      gap,
      stride,
      x,
      y,
    };
  }, [days, range]);
  const displayedCount = hoveredDay?.count ?? heatmap.totalEvents;
  const displayedLabel = hoveredDay?.label ?? RANGE_SUMMARY[range];

  return (
    <div className="flex flex-col gap-3">
      {/* Header */}
      <div className="flex items-center justify-between">
        <div className="flex items-center gap-2">
          <LayoutGrid className="h-4 w-4" style={{ color: "rgba(16,185,129,1)" }} />
          <span className="text-sm font-semibold text-strong">Activity Heatmap</span>
          <span className="ml-1 rounded px-1.5 py-0.5 text-[11px] font-semibold tabular-nums"
            style={{background:"rgba(16,185,129,0.14)", color:GREEN}}>
            {displayedCount.toLocaleString()}
          </span>
          <span className="hidden text-xs sm:inline" style={{color:W40}}>
            {hoveredDay ? `on ${displayedLabel}` : `in ${displayedLabel}`}
          </span>
        </div>
        <RangeSelect
          value={range}
          onValueChange={onRangeChange}
          accent="green"
          ariaLabel="Select Activity Heatmap range"
        />
      </div>

      {/* SVG */}
      <svg viewBox={`0 0 ${LVW} ${HVH}`} width="100%" style={{ display:"block", height:LVH }} aria-label={`Activity heatmap last ${range}`}>
        {heatmap.mode === "strip" ? (
          <>
            {heatmap.daily.map((day, i) => (
              <g key={day.date}>
                <rect
                  x={heatmap.x + i * (heatmap.cell + heatmap.gap)}
                  y={heatmap.y}
                  width={heatmap.cell}
                  height={heatmap.cell}
                  rx={4}
                  tabIndex={0}
                  onMouseEnter={() => setHoveredDay(day)}
                  onFocus={() => setHoveredDay(day)}
                  onMouseLeave={() => setHoveredDay(null)}
                  onBlur={() => setHoveredDay(null)}
                  style={{
                    fill: heatFill(day.count, heatmap.maxCount),
                    stroke: hoveredDay?.date === day.date ? GREEN : "transparent",
                    strokeWidth: 2,
                    outline: "none",
                  }}
                >
                  <title>{day.date}{day.count>0?`: ${day.count} event${day.count!==1?"s":""}` : ": no activity"}</title>
                </rect>
                {(range === "7d" || i % 5 === 0 || i === heatmap.daily.length - 1) && (
                  <text
                    x={heatmap.x + i * (heatmap.cell + heatmap.gap) + heatmap.cell / 2}
                    y={heatmap.y + heatmap.cell + 17}
                    fontSize={10}
                    textAnchor="middle"
                    style={{fill:W70}}
                  >
                    {day.label}
                  </text>
                )}
              </g>
            ))}
          </>
        ) : (
          <>
            {heatmap.monthLabels.map(({weekIdx, label}) => (
              <text key={`m-${weekIdx}`} x={heatmap.x+weekIdx*heatmap.stride} y={15} fontSize={10} style={{fill:W70}}>{label}</text>
            ))}
            {(["Mon","Wed","Fri"] as const).map((label, i) => (
              <text key={label} x={0} y={heatmap.y+(i*2+1)*heatmap.stride+heatmap.cell-2} fontSize={10} style={{fill:W70}}>{label}</text>
            ))}
            {heatmap.weeks.map((week, wi) =>
              week.map((cell, di) => cell.outsideRange ? null : (
                <rect key={cell.date}
                  x={heatmap.x+wi*heatmap.stride} y={heatmap.y+di*heatmap.stride}
                  width={heatmap.cell} height={heatmap.cell} rx={2}
                  tabIndex={0}
                  onMouseEnter={() => setHoveredDay({ date: cell.date, count: cell.count, label: cell.label })}
                  onFocus={() => setHoveredDay({ date: cell.date, count: cell.count, label: cell.label })}
                  onMouseLeave={() => setHoveredDay(null)}
                  onBlur={() => setHoveredDay(null)}
                  style={{
                    fill: heatFill(cell.count, heatmap.maxCount),
                    stroke: hoveredDay?.date === cell.date ? GREEN : "transparent",
                    strokeWidth: 1.5,
                    outline: "none",
                  }}>
                  <title>{cell.date}{cell.count>0?`: ${cell.count} event${cell.count!==1?"s":""}` : ": no activity"}</title>
                </rect>
              ))
            )}
          </>
        )}
        <text x={HLEFT} y={HVH-9} fontSize={9} style={{fill:W40}}>Less</text>
        {HMAP_FILL.map((fill, i) => (
          <rect key={i} x={HLEFT+32+i*14} y={HVH-20} width={11} height={11} rx={2} style={{fill}}/>
        ))}
        <text x={HLEFT+32+HMAP_FILL.length*14+2} y={HVH-9} fontSize={9} style={{fill:W40}}>More</text>
      </svg>

      {/* Contributions summary */}
      <p className="text-center">
        <span className="text-2xl font-bold tabular-nums" style={{color:GREEN}}>
          {heatmap.totalEvents.toLocaleString()}
        </span>
        <span className="ml-1.5 text-sm" style={{color:W70}}>
          contributions across all tracked repositories in the last {RANGE_SUMMARY[range]}
        </span>
      </p>

    </div>
  );
}

// ── Main export ────────────────────────────────────────────────────────────
export function ForgejoHeatmap({
  days,
  totalAdditions = 0,
  totalDeletions = 0,
  hasLineStats   = false,
  lastPush       = null,
  isLoading      = false,
}: ForgejoHeatmapProps) {
  const [lineRange, setLineRange] = useState<RangeKey>("14d");
  const [heatRange, setHeatRange] = useState<RangeKey>("14d");

  if (isLoading) {
    return (
      <div className="grid grid-cols-1 gap-4 lg:grid-cols-2">
        {[0,1].map(i => (
          <div key={i} className="rounded-xl border border-[color:var(--border)] bg-[color:var(--surface)] p-4">
            <div className="mb-3 flex justify-between">
              <div className="h-4 w-28 animate-pulse rounded" style={{background:"rgba(255,255,255,0.08)"}}/>
              <div className="h-4 w-20 animate-pulse rounded" style={{background:"rgba(255,255,255,0.08)"}}/>
            </div>
            <div className="animate-pulse rounded" style={{height:LVH,background:"rgba(255,255,255,0.05)"}}/>
          </div>
        ))}
      </div>
    );
  }

  return (
    <div className="space-y-4">

      {/* ── Two cards ───────────────────────────────────────────── */}
      <div className="grid grid-cols-1 gap-4 lg:grid-cols-2">
        <section className="rounded-xl border border-[color:var(--border)] bg-[color:var(--surface)] p-4 shadow-lush">
          <LineChart days={days} range={lineRange} onRangeChange={setLineRange} lastPush={lastPush} />
        </section>
        <section className="rounded-xl border border-[color:var(--border)] bg-[color:var(--surface)] p-4 shadow-lush">
          <HeatmapGrid days={days} range={heatRange} onRangeChange={setHeatRange} />
        </section>
      </div>

      {/* ── Line stats — boxed ──────────────────────────────────── */}
      {hasLineStats ? (
        <div className="mx-auto flex max-w-xs items-center justify-center gap-8 rounded-xl px-8 py-4"
          style={{background:"rgba(139,92,246,0.07)", border:"1px solid rgba(139,92,246,0.20)"}}>
          <div className="flex flex-col items-center gap-0.5">
            <span className="text-2xl font-bold tabular-nums" style={{color:GREEN}}>+{fmtLines(totalAdditions)}</span>
            <span className="text-xs font-medium" style={{color:"rgba(52,211,153,0.70)"}}>lines of code added</span>
          </div>
          <div style={{width:1,height:40,background:"rgba(139,92,246,0.25)"}}/>
          <div className="flex flex-col items-center gap-0.5">
            <span className="text-2xl font-bold tabular-nums" style={{color:RED}}>-{fmtLines(totalDeletions)}</span>
            <span className="text-xs font-medium" style={{color:"rgba(248,113,113,0.70)"}}>lines removed</span>
          </div>
        </div>
      ) : (
        <p className="text-center text-xs" style={{color:W70,opacity:0.6}}>
          Line stats syncing with Forgejo — will appear on next refresh
        </p>
      )}

    </div>
  );
}
