"use client";

import gsap from "gsap";
import { useId, useLayoutEffect, useMemo, useRef, useState } from "react";
import { AnimatedCounter } from "./animated-counter";
import { PulseIcon, TrendUpIcon } from "./icons";

export type DailyPoint = { date: string; label: string; count: number };

export type DailyChartAccent = {
  /** Three stops for the line gradient (left → middle → right). */
  line: [string, string, string];
  /** Solid colour used for the area fill, dots and pulse. */
  fill: string;
};

type Props = {
  title: string;
  subtitle: string;
  data: DailyPoint[];
  isLoading: boolean;
  accent?: DailyChartAccent;
  unit?: { singular: string; plural: string };
  loadingLabel?: string;
};

const W = 1000;
const H = 240;
const PAD_L = 0;
const PAD_R = 0;
const PAD_T = 24;
const PAD_B = 36;

const DEFAULT_ACCENT: DailyChartAccent = {
  line: ["#5750F1", "#8155FF", "#0ABEF9"],
  fill: "#5750F1",
};

export function DailyTrendChart({
  title,
  subtitle,
  data,
  isLoading,
  accent = DEFAULT_ACCENT,
  unit = { singular: "item", plural: "items" },
  loadingLabel = "Loading…",
}: Props) {
  const rootRef = useRef<HTMLDivElement>(null);
  const svgRef = useRef<SVGSVGElement>(null);
  const pathRef = useRef<SVGPathElement>(null);
  const areaRef = useRef<SVGPathElement>(null);
  const dotsRef = useRef<SVGGElement>(null);

  // Unique gradient/filter ids so multiple charts can live on one page.
  const uid = useId().replace(/[^a-zA-Z0-9]/g, "");
  const fillId = `dtFill-${uid}`;
  const lineId = `dtLine-${uid}`;
  const glowId = `dtGlow-${uid}`;

  const [hoverIdx, setHoverIdx] = useState<number | null>(null);

  const { linePath, areaPath, points, sumY } = useMemo(() => {
    const max = Math.max(1, ...data.map((d) => d.count));
    const innerW = W - PAD_L - PAD_R;
    const innerH = H - PAD_T - PAD_B;
    const step = data.length > 1 ? innerW / (data.length - 1) : innerW;

    const pts = data.map((d, i) => ({
      x: PAD_L + i * step,
      y: PAD_T + innerH - (d.count / max) * innerH,
      d,
    }));

    if (pts.length === 0) {
      return { linePath: "", areaPath: "", points: pts, sumY: 0 };
    }

    const line = pts
      .map((p, i) => `${i === 0 ? "M" : "L"} ${p.x.toFixed(2)} ${p.y.toFixed(2)}`)
      .join(" ");

    const area = `${line} L ${pts[pts.length - 1].x.toFixed(2)} ${PAD_T + innerH} L ${pts[0].x.toFixed(2)} ${PAD_T + innerH} Z`;

    return {
      linePath: line,
      areaPath: area,
      points: pts,
      sumY: data.reduce((acc, p) => acc + p.count, 0),
    };
  }, [data]);

  useLayoutEffect(() => {
    const reduced =
      typeof window !== "undefined" &&
      window.matchMedia("(prefers-reduced-motion: reduce)").matches;

    const ctx = gsap.context(() => {
      if (reduced) return;
      gsap.from(rootRef.current, {
        y: 24,
        opacity: 0,
        duration: 0.6,
        ease: "power3.out",
        delay: 0.2,
      });

      if (pathRef.current && linePath) {
        const length = pathRef.current.getTotalLength();
        gsap.fromTo(
          pathRef.current,
          { strokeDasharray: length, strokeDashoffset: length },
          {
            strokeDashoffset: 0,
            duration: 1.4,
            ease: "power2.inOut",
            delay: 0.3,
          },
        );
      }
      if (areaRef.current) {
        gsap.fromTo(
          areaRef.current,
          { opacity: 0, y: 12 },
          { opacity: 1, y: 0, duration: 1, delay: 0.5, ease: "power2.out" },
        );
      }
      if (dotsRef.current) {
        gsap.fromTo(
          dotsRef.current.querySelectorAll("circle"),
          { scale: 0, transformOrigin: "center" },
          {
            scale: 1,
            duration: 0.4,
            stagger: 0.05,
            ease: "back.out(2)",
            delay: 0.8,
          },
        );
      }
    }, rootRef);
    return () => ctx.revert();
  }, [linePath]);

  const last = points[points.length - 1]?.d.count ?? 0;
  const prev = points[points.length - 2]?.d.count ?? 0;
  const dayDelta = last - prev;
  const avg = data.length ? Math.round(sumY / data.length) : 0;

  const handleMove = (e: React.MouseEvent<SVGSVGElement>) => {
    if (!svgRef.current || !points.length) return;
    const rect = svgRef.current.getBoundingClientRect();
    const px = ((e.clientX - rect.left) / rect.width) * W;
    let bestI = 0;
    let bestD = Infinity;
    for (let i = 0; i < points.length; i++) {
      const d = Math.abs(points[i].x - px);
      if (d < bestD) {
        bestD = d;
        bestI = i;
      }
    }
    setHoverIdx(bestI);
  };

  const hovered = hoverIdx !== null ? points[hoverIdx] : null;
  const innerH = H - PAD_T - PAD_B;
  const tooltipLeftPct = hovered ? (hovered.x / W) * 100 : 0;
  const tooltipTopPct = hovered ? (hovered.y / H) * 100 : 0;
  const flipLeft = hovered ? hovered.x > W * 0.7 : false;
  const flipRight = hovered ? hovered.x < W * 0.12 : false;
  const flipDown = hovered ? hovered.y < H * 0.35 : false;
  const tooltipTX = flipLeft ? "-100%" : flipRight ? "0%" : "-50%";
  const tooltipTY = flipDown ? "calc(0% + 14px)" : "calc(-100% - 14px)";

  return (
    <div
      ref={rootRef}
      className="relative overflow-hidden rounded-2xl bg-white p-5 shadow-1 ring-1 ring-stroke dark:bg-gray-dark dark:ring-stroke-dark dark:shadow-card md:p-6"
    >
      <div className="flex flex-wrap items-start justify-between gap-3">
        <div>
          <h2 className="text-lg font-bold text-dark dark:text-white">
            {title}
          </h2>
          <p className="mt-1 text-sm text-dark-5 dark:text-dark-6">{subtitle}</p>
        </div>
        <div className="flex items-center gap-3">
          <div className="rounded-xl border border-stroke px-3 py-1.5 dark:border-stroke-dark">
            <div className="text-[11px] uppercase tracking-wider text-dark-6">
              Today
            </div>
            <div className="text-base font-bold text-dark dark:text-white">
              <AnimatedCounter value={last} />
            </div>
          </div>
          <div className="rounded-xl border border-stroke px-3 py-1.5 dark:border-stroke-dark">
            <div className="text-[11px] uppercase tracking-wider text-dark-6">
              Avg/day
            </div>
            <div className="text-base font-bold text-dark dark:text-white">
              <AnimatedCounter value={avg} />
            </div>
          </div>
          <div
            className="hidden items-center gap-1.5 rounded-full px-3 py-1 text-xs font-medium sm:inline-flex"
            style={{ backgroundColor: `${accent.fill}1a`, color: accent.fill }}
          >
            <TrendUpIcon className="size-3.5" />
            {dayDelta >= 0 ? "+" : ""}
            {dayDelta} vs yesterday
          </div>
        </div>
      </div>

      <div className="mt-4 w-full overflow-x-auto">
        {isLoading ? (
          <div className="flex h-[240px] items-center justify-center text-sm text-dark-6">
            <PulseIcon className="mr-2 size-4 animate-pulse" /> {loadingLabel}
          </div>
        ) : (
          <div className="relative h-[240px] min-w-[600px]">
            <svg
              ref={svgRef}
              viewBox={`0 0 ${W} ${H}`}
              preserveAspectRatio="none"
              className="h-full w-full"
              style={{ overflow: "visible" }}
              onMouseMove={handleMove}
              onMouseLeave={() => setHoverIdx(null)}
            >
              <defs>
                <linearGradient id={fillId} x1="0" x2="0" y1="0" y2="1">
                  <stop offset="0%" stopColor={accent.fill} stopOpacity="0.5" />
                  <stop offset="100%" stopColor={accent.fill} stopOpacity="0" />
                </linearGradient>
                <linearGradient id={lineId} x1="0" x2="1" y1="0" y2="0">
                  <stop offset="0%" stopColor={accent.line[0]} />
                  <stop offset="50%" stopColor={accent.line[1]} />
                  <stop offset="100%" stopColor={accent.line[2]} />
                </linearGradient>
                <filter id={glowId} x="-20%" y="-20%" width="140%" height="140%">
                  <feGaussianBlur stdDeviation="4" result="blur" />
                  <feMerge>
                    <feMergeNode in="blur" />
                    <feMergeNode in="SourceGraphic" />
                  </feMerge>
                </filter>
              </defs>

              {[0.25, 0.5, 0.75].map((p) => (
                <line
                  key={p}
                  x1={PAD_L}
                  x2={W - PAD_R}
                  y1={PAD_T + innerH * p}
                  y2={PAD_T + innerH * p}
                  stroke="currentColor"
                  className="text-stroke dark:text-stroke-dark"
                  strokeDasharray="4 6"
                  strokeWidth="1"
                />
              ))}

              {areaPath && (
                <path ref={areaRef} d={areaPath} fill={`url(#${fillId})`} />
              )}
              {linePath && (
                <>
                  <path
                    d={linePath}
                    fill="none"
                    stroke={`url(#${lineId})`}
                    strokeWidth="6"
                    strokeOpacity="0.35"
                    strokeLinecap="round"
                    strokeLinejoin="round"
                    filter={`url(#${glowId})`}
                    vectorEffect="non-scaling-stroke"
                  />
                  <path
                    ref={pathRef}
                    d={linePath}
                    fill="none"
                    stroke={`url(#${lineId})`}
                    strokeWidth="3"
                    strokeLinecap="round"
                    strokeLinejoin="round"
                    vectorEffect="non-scaling-stroke"
                  />
                </>
              )}

              {hovered && (
                <line
                  x1={hovered.x}
                  x2={hovered.x}
                  y1={PAD_T}
                  y2={PAD_T + innerH}
                  stroke={accent.fill}
                  strokeOpacity="0.35"
                  strokeWidth="1"
                  strokeDasharray="3 4"
                  vectorEffect="non-scaling-stroke"
                />
              )}

              <g ref={dotsRef}>
                {points.map((p, i) => {
                  const isHover = hoverIdx === i;
                  const isLatest = i === points.length - 1;
                  return (
                    <g key={i}>
                      {isLatest && (
                        <>
                          <circle
                            cx={p.x}
                            cy={p.y}
                            r="11"
                            fill={accent.fill}
                            opacity="0.25"
                          >
                            <animate
                              attributeName="r"
                              values="6;18;6"
                              dur="2.4s"
                              repeatCount="indefinite"
                            />
                            <animate
                              attributeName="opacity"
                              values="0.35;0;0.35"
                              dur="2.4s"
                              repeatCount="indefinite"
                            />
                          </circle>
                          <circle
                            cx={p.x}
                            cy={p.y}
                            r="7"
                            fill={accent.fill}
                            opacity="0.35"
                          />
                        </>
                      )}
                      <circle
                        cx={p.x}
                        cy={p.y}
                        r={isHover ? 7 : isLatest ? 5 : 4}
                        fill={isHover || isLatest ? accent.fill : "white"}
                        stroke={accent.fill}
                        strokeWidth={isHover ? 3 : 2}
                        className="transition-[r] duration-150 drop-shadow-sm"
                        vectorEffect="non-scaling-stroke"
                      />
                    </g>
                  );
                })}
              </g>

              {points.map((p, i) => {
                const isFirst = i === 0;
                const isLast = i === points.length - 1;
                if (!(isFirst || isLast || i % 2 === 0)) return null;
                return (
                  <text
                    key={i}
                    x={p.x}
                    y={H - 12}
                    textAnchor={isFirst ? "start" : isLast ? "end" : "middle"}
                    className="fill-dark-6 text-[10px]"
                  >
                    {p.d.label}
                  </text>
                );
              })}
            </svg>

            {hovered && (
              <div
                className="pointer-events-none absolute z-10 rounded-xl border border-stroke bg-white px-3 py-2 text-xs shadow-lg dark:border-stroke-dark dark:bg-gray-dark"
                style={{
                  left: `${tooltipLeftPct}%`,
                  top: `${tooltipTopPct}%`,
                  transform: `translate(${tooltipTX}, ${tooltipTY})`,
                }}
              >
                <div className="font-semibold uppercase tracking-wider text-dark-6">
                  {hovered.d.label}
                </div>
                <div className="mt-1 flex items-baseline gap-1.5">
                  <span className="text-lg font-bold text-dark dark:text-white tabular-nums">
                    {hovered.d.count}
                  </span>
                  <span className="text-[11px] text-dark-6">
                    {hovered.d.count === 1 ? unit.singular : unit.plural}
                  </span>
                </div>
                {hoverIdx !== null && hoverIdx > 0 && (
                  <div className="mt-1 text-[10px] text-dark-6">
                    {(() => {
                      const diff =
                        hovered.d.count - points[hoverIdx - 1].d.count;
                      const sign = diff > 0 ? "+" : "";
                      return `${sign}${diff} vs previous day`;
                    })()}
                  </div>
                )}
              </div>
            )}
          </div>
        )}
      </div>
    </div>
  );
}
