import React, { useEffect, useMemo, useRef, useState } from "react";

// Year-in-Pixels Mood Tracker
// - 12 columns (months)
// - 31 rows (days)
// - Each cell = one calendar day for the selected year
// - 3 moods: Green (G), Amber (A), Red (R)
// - Data persists to localStorage per-year
// - Click to cycle Empty → G → A → R → Empty
// - Drag painting with a selected color (optional): pick a color, then drag
// - Export/Import JSON, Export CSV, Print
// - Highlights today's date when viewing the current year

// Tailwind color classes for moods
const MOOD_STYLES: Record<string, string> = {
  "": "bg-zinc-200 dark:bg-zinc-700",
  G: "bg-green-500",
  A: "bg-amber-400",
  R: "bg-red-500",
};

const MOOD_LABELS: Record<string, string> = {
  "": "No entry",
  G: "Green",
  A: "Amber",
  R: "Red",
};

const MONTHS = [
  "Jan",
  "Feb",
  "Mar",
  "Apr",
  "May",
  "Jun",
  "Jul",
  "Aug",
  "Sep",
  "Oct",
  "Nov",
  "Dec",
];

function pad(n: number) {
  return String(n).padStart(2, "0");
}

function daysInMonth(year: number, monthIndex0: number) {
  // monthIndex0 = 0..11
  return new Date(year, monthIndex0 + 1, 0).getDate();
}

function ymd(year: number, m0: number, d: number) {
  return `${year}-${pad(m0 + 1)}-${pad(d)}`;
}

function download(filename: string, contents: string, type = "text/plain") {
  const blob = new Blob([contents], { type });
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url;
  a.download = filename;
  document.body.appendChild(a);
  a.click();
  a.remove();
  URL.revokeObjectURL(url);
}

export default function YearInPixelsMoodTracker() {
  const now = new Date();
  const [year, setYear] = useState<number>(now.getFullYear());
  const [moods, setMoods] = useState<Record<string, "" | "G" | "A" | "R">>({});
  const [paintColor, setPaintColor] = useState<"" | "G" | "A" | "R">("");
  const [showNumbers, setShowNumbers] = useState(true);
  const [compact, setCompact] = useState(false);
  const [showLegend, setShowLegend] = useState(true);

  // drag-paint state
  const isDraggingRef = useRef(false);
  const paintColorRef = useRef<"" | "G" | "A" | "R">(paintColor);
  useEffect(() => {
    paintColorRef.current = paintColor;
  }, [paintColor]);

  // Load/save from localStorage
  const storageKey = useMemo(() => `year-in-pixels-mood-${year}`, [year]);

  useEffect(() => {
    try {
      const raw = localStorage.getItem(storageKey);
      if (raw) {
        if (raw.length > 100000) {
          console.error("Stored data too large, clearing");
          localStorage.removeItem(storageKey);
          setMoods({});
          return;
        }
        
        const parsed = JSON.parse(raw);
        if (parsed && typeof parsed === "object") {
          const validatedMoods: Record<string, "" | "G" | "A" | "R"> = {};
          
          for (const [key, value] of Object.entries(parsed)) {
            if (typeof key === "string" && /^\d{4}-\d{2}-\d{2}$/.test(key)) {
              if (value === "" || value === "G" || value === "A" || value === "R") {
                validatedMoods[key] = value as "" | "G" | "A" | "R";
              }
            }
          }
          
          setMoods(validatedMoods);
        } else {
          setMoods({});
        }
      } else {
        setMoods({});
      }
    } catch (e) {
      console.error("Failed to load moods", e);
      localStorage.removeItem(storageKey);
      setMoods({});
    }
  }, [storageKey]);

  useEffect(() => {
    try {
      localStorage.setItem(storageKey, JSON.stringify(moods));
    } catch (e) {
      console.error("Failed to save moods", e);
    }
  }, [moods, storageKey]);

  // Helpers
  const todayKey = useMemo(() => {
    const t = new Date();
    return ymd(t.getFullYear(), t.getMonth(), t.getDate());
  }, []);

  function cycleMood(curr: "" | "G" | "A" | "R") {
    if (curr === "") return "G";
    if (curr === "G") return "A";
    if (curr === "A") return "R";
    return ""; // R → empty
  }

  function setMoodFor(key: string, value: "" | "G" | "A" | "R") {
    setMoods((prev) => ({ ...prev, [key]: value }));
  }

  function handleCellClick(key: string) {
    if (paintColor) {
      setMoodFor(key, paintColor);
    } else {
      setMoods((prev) => ({ ...prev, [key]: cycleMood(prev[key] || "") }));
    }
  }

  function handlePointerDown(e: React.PointerEvent, key: string) {
    (e.target as HTMLElement).setPointerCapture?.(e.pointerId);
    isDraggingRef.current = true;
    if (paintColorRef.current) {
      setMoodFor(key, paintColorRef.current);
    } else {
      setMoods((prev) => ({ ...prev, [key]: cycleMood(prev[key] || "") }));
    }
  }

  function handlePointerEnter(_e: React.PointerEvent, key: string) {
    if (!isDraggingRef.current) return;
    if (paintColorRef.current) setMoodFor(key, paintColorRef.current);
  }

  function handlePointerUp() {
    isDraggingRef.current = false;
  }

  // Stats
  const stats = useMemo(() => {
    const counts = { G: 0, A: 0, R: 0 } as Record<"G" | "A" | "R", number>;
    for (const [k, v] of Object.entries(moods)) {
      if (!k.startsWith(`${year}-`)) continue;
      if (v === "G" || v === "A" || v === "R") counts[v]++;
    }
    return counts;
  }, [moods, year]);

  function resetYear() {
    const next = { ...moods };
    Object.keys(next)
      .filter((k) => k.startsWith(`${year}-`))
      .forEach((k) => delete next[k]);
    setMoods(next);
  }

  function exportJSON() {
    const data: Record<string, string> = {};
    Object.entries(moods)
      .filter(([k]) => k.startsWith(`${year}-`))
      .forEach(([k, v]) => (data[k] = v));
    download(`mood-${year}.json`, JSON.stringify({ year, data }, null, 2), "application/json");
  }

  function exportCSV() {
    const rows: string[] = ["date,month,day,mood"];
    for (let m = 0; m < 12; m++) {
      const dim = daysInMonth(year, m);
      for (let d = 1; d <= dim; d++) {
        const key = ymd(year, m, d);
        const mood = moods[key] || "";
        rows.push(`${key},${MONTHS[m]},${d},${mood}`);
      }
    }
    download(`mood-${year}.csv`, rows.join("\n"), "text/csv");
  }

  function importJSON(file: File) {
    if (file.size > 1024 * 1024) {
      alert("File too large. Maximum size is 1MB.");
      return;
    }

    if (!file.type.includes("json") && !file.name.endsWith(".json")) {
      alert("Please select a valid JSON file.");
      return;
    }

    const reader = new FileReader();
    reader.onload = () => {
      try {
        const result = String(reader.result);
        if (result.length > 1024 * 1024) {
          alert("File content too large.");
          return;
        }
        
        const parsed = JSON.parse(result);
        if (parsed && typeof parsed === "object" && parsed.data) {
          const validatedData: Record<string, "" | "G" | "A" | "R"> = {};
          
          for (const [key, value] of Object.entries(parsed.data)) {
            if (typeof key === "string" && /^\d{4}-\d{2}-\d{2}$/.test(key)) {
              if (value === "" || value === "G" || value === "A" || value === "R") {
                validatedData[key] = value as "" | "G" | "A" | "R";
              }
            }
          }
          
          setMoods((prev) => ({ ...prev, ...validatedData }));
          if (parsed.year && typeof parsed.year === "number" && parsed.year >= 1900 && parsed.year <= 3000) {
            setYear(parsed.year);
          }
        } else {
          alert("Invalid JSON format.");
        }
      } catch (e) {
        alert("Failed to parse JSON. Please ensure the file is valid JSON.");
      }
    };
    reader.readAsText(file);
  }

  // Rendering helpers
  const Cell = ({ m0, d }: { m0: number; d: number }) => {
    const dim = daysInMonth(year, m0);
    const active = d <= dim;
    const key = ymd(year, m0, d);
    const mood = moods[key] || "";

    if (!active) {
      return (
        <div
          className="w-7 h-7 sm:w-8 sm:h-8 md:w-9 md:h-9 rounded-md bg-zinc-100 dark:bg-zinc-800 opacity-50"
          aria-hidden
        />
      );
    }

    const isToday = key === todayKey;

    return (
      <button
        aria-label={`${year}-${MONTHS[m0]} ${d}: ${MOOD_LABELS[mood]}`}
        title={`${year}-${pad(m0 + 1)}-${pad(d)} (${MOOD_LABELS[mood]})`}
        onClick={() => handleCellClick(key)}
        onPointerDown={(e) => handlePointerDown(e, key)}
        onPointerEnter={(e) => handlePointerEnter(e, key)}
        onPointerUp={handlePointerUp}
        className={[
          "group w-7 h-7 sm:w-8 sm:h-8 md:w-9 md:h-9 rounded-md border border-zinc-300/60 dark:border-zinc-600/60 flex items-center justify-center select-none",
          MOOD_STYLES[mood],
          isToday && year === new Date().getFullYear()
            ? "ring-2 ring-offset-1 ring-black/40 dark:ring-white/50"
            : "",
        ].join(" ")}
      >
        {showNumbers && (
          <span className="text-[10px] sm:text-[11px] md:text-xs font-medium text-black/80 dark:text-white/90">
            {d}
          </span>
        )}
      </button>
    );
  };

  return (
    <div className="min-h-screen w-full bg-white dark:bg-zinc-950 text-zinc-900 dark:text-zinc-50 p-4 md:p-6">
      <div className="mx-auto max-w-6xl">
        {/* Header */}
        <div className="flex flex-col md:flex-row md:items-end md:justify-between gap-4 mb-6">
          <div>
            <h1 className="text-2xl md:text-3xl font-bold tracking-tight">Year in Pixels – Mood (RAG)</h1>
            <p className="text-sm text-zinc-600 dark:text-zinc-400 mt-1">
              12 columns (months) × 31 rows (days). Click to cycle or select a color to paint and drag across days.
            </p>
          </div>

          {/* Controls */}
          <div className="flex flex-wrap items-center gap-2">
            <div className="flex items-center gap-2 border border-zinc-300 dark:border-zinc-700 rounded-xl p-2">
              <button
                className="px-2 py-1 rounded-lg border border-zinc-300 dark:border-zinc-700"
                onClick={() => setYear((y) => y - 1)}
                aria-label="Previous year"
              >
                ◀
              </button>
              <input
                type="number"
                className="w-24 px-3 py-1 rounded-lg border border-zinc-300 dark:border-zinc-700 bg-transparent text-center"
                value={year}
                onChange={(e) => setYear(parseInt(e.target.value || `${now.getFullYear()}`, 10))}
                aria-label="Year"
              />
              <button
                className="px-2 py-1 rounded-lg border border-zinc-300 dark:border-zinc-700"
                onClick={() => setYear((y) => y + 1)}
                aria-label="Next year"
              >
                ▶
              </button>
            </div>

            {/* Paint palette */}
            <div className="flex items-center gap-2 border border-zinc-300 dark:border-zinc-700 rounded-xl p-2">
              <span className="text-xs mr-1">Paint:</span>
              {(
                [
                  { k: "", name: "Off", cls: "bg-zinc-200 dark:bg-zinc-700" },
                  { k: "G", name: "Green", cls: "bg-green-500" },
                  { k: "A", name: "Amber", cls: "bg-amber-400" },
                  { k: "R", name: "Red", cls: "bg-red-500" },
                ] as const
              ).map((opt) => (
                <button
                  key={opt.k}
                  className={[
                    "w-7 h-7 rounded-lg border border-zinc-300/70 dark:border-zinc-600/70 flex items-center justify-center",
                    opt.cls,
                    paintColor === opt.k ? "ring-2 ring-black/40 dark:ring-white/70" : "",
                  ].join(" ")}
                  onClick={() => setPaintColor(opt.k)}
                  title={`Paint: ${opt.name}`}
                  aria-label={`Paint color: ${opt.name}`}
                >
                  {opt.k === "" ? (
                    <span className="text-[10px] font-medium text-zinc-800 dark:text-zinc-100">Off</span>
                  ) : null}
                </button>
              ))}
            </div>

            {/* Toggles */}
            <div className="flex items-center gap-2 border border-zinc-300 dark:border-zinc-700 rounded-xl p-2">
              <label className="flex items-center gap-1 text-xs">
                <input
                  type="checkbox"
                  checked={showNumbers}
                  onChange={(e) => setShowNumbers(e.target.checked)}
                />
                Day numbers
              </label>
              <label className="flex items-center gap-1 text-xs">
                <input
                  type="checkbox"
                  checked={compact}
                  onChange={(e) => setCompact(e.target.checked)}
                />
                Compact
              </label>
              <label className="flex items-center gap-1 text-xs">
                <input
                  type="checkbox"
                  checked={showLegend}
                  onChange={(e) => setShowLegend(e.target.checked)}
                />
                Legend
              </label>
            </div>

            {/* Actions */}
            <div className="flex items-center gap-2 border border-zinc-300 dark:border-zinc-700 rounded-xl p-2">
              <button className="px-3 py-1.5 rounded-lg border border-zinc-300 dark:border-zinc-700" onClick={exportJSON}>
                Export JSON
              </button>
              <button className="px-3 py-1.5 rounded-lg border border-zinc-300 dark:border-zinc-700" onClick={exportCSV}>
                Export CSV
              </button>
              <label className="px-3 py-1.5 rounded-lg border border-zinc-300 dark:border-zinc-700 cursor-pointer">
                Import JSON
                <input type="file" accept="application/json" className="hidden" onChange={(e) => {
                  const f = e.target.files?.[0];
                  if (f) importJSON(f);
                  e.currentTarget.value = "";
                }} />
              </label>
              <button className="px-3 py-1.5 rounded-lg border border-zinc-300 dark:border-zinc-700" onClick={resetYear}>
                Reset year
              </button>
              <button className="px-3 py-1.5 rounded-lg border border-zinc-300 dark:border-zinc-700" onClick={() => window.print()}>
                Print
              </button>
            </div>
          </div>
        </div>

        {/* Legend */}
        {showLegend && (
          <div className="mb-4 text-sm flex flex-wrap items-center gap-3">
            <div className="flex items-center gap-1.5"><span className="inline-block w-3.5 h-3.5 rounded-sm bg-green-500" /> Green</div>
            <div className="flex items-center gap-1.5"><span className="inline-block w-3.5 h-3.5 rounded-sm bg-amber-400" /> Amber</div>
            <div className="flex items-center gap-1.5"><span className="inline-block w-3.5 h-3.5 rounded-sm bg-red-500" /> Red</div>
            <div className="flex items-center gap-1.5"><span className="inline-block w-3.5 h-3.5 rounded-sm bg-zinc-200 dark:bg-zinc-700" /> No entry</div>
            <div className="flex items-center gap-1.5"><span className="inline-block w-3.5 h-3.5 rounded-sm ring-2 ring-black/40 dark:ring-white/70" /> Today</div>
          </div>
        )}

        {/* Summary */}
        <div className="mb-6 text-sm text-zinc-700 dark:text-zinc-300">
          <span className="mr-4">Green: <strong>{stats.G}</strong></span>
          <span className="mr-4">Amber: <strong>{stats.A}</strong></span>
          <span className="mr-4">Red: <strong>{stats.R}</strong></span>
        </div>

        {/* Month headers */}
        <div className="grid grid-cols-12 gap-1 pl-8 sm:pl-10 md:pl-12 mb-2">
          {MONTHS.map((m) => (
            <div
              key={m}
              className="justify-self-start w-7 sm:w-8 md:w-9 text-center text-[11px] sm:text-sm font-medium text-zinc-600 dark:text-zinc-400"
            >
              {m}
            </div>
          ))}
        </div>

        {/* Grid wrapper with day labels at left */}
        <div className={[
          "relative",
          compact ? "-mt-1" : "",
        ].join(" ")}>
          <div className="absolute -left-0 top-0 flex flex-col gap-1 pr-2">
            {Array.from({ length: 31 }).map((_, i) => (
              <div
                key={i}
                className="h-7 sm:h-8 md:h-9 w-8 sm:w-10 md:w-12 text-[10px] sm:text-xs md:text-sm text-right pr-1 text-zinc-500 dark:text-zinc-400 flex items-center justify-end"
              >
                {i + 1}
              </div>
            ))}
          </div>

          {/* The 31 × 12 day grid */}
          <div className="grid grid-cols-12 gap-1 pl-8 sm:pl-10 md:pl-12"
            onPointerUp={handlePointerUp}
            onPointerCancel={handlePointerUp}
            onPointerLeave={handlePointerUp}
          >
            {Array.from({ length: 31 }).map((_, dIndex) => (
              <React.Fragment key={dIndex}>
                {Array.from({ length: 12 }).map((_, mIndex) => (
                  <Cell key={`${mIndex}-${dIndex}`} m0={mIndex} d={dIndex + 1} />
                ))}
              </React.Fragment>
            ))}
          </div>
        </div>

        {/* Footer note */}
        <div className="mt-8 text-xs text-zinc-500 dark:text-zinc-400">
          Tip: Turn on a paint color to quickly fill multiple days by dragging across the grid. Toggle “Day numbers” for a cleaner look.
        </div>
      </div>
    </div>
  );
}
