import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { useToast } from "@/components/ui/use-toast";
import { Share2, Download, Shuffle, Copy, Link as LinkIcon } from "lucide-react";
import * as htmlToImage from "html-to-image";
import { saveAs } from "file-saver";
import { compressToEncodedURIComponent, decompressFromEncodedURIComponent } from "lz-string";
import SocialShare from "@/components/SocialShare";
import { cn } from "@/lib/utils";

// Types
export type NodeType = "bone" | "flower" | "carrot" | "well" | "blank";

interface Node {
  id: string;
  type: NodeType;
  x: number;
  y: number;
}

type EdgeCond = { kind: "somebodyAt" | "nobodyAt"; type: NodeType };

interface Edge {
  id: string;
  from: string;
  to: string;
  conditions: EdgeCond[];
}

interface PuzzleSpec {
  nodes: Node[];
  edges: Edge[];
  start: { dog: string; bunnies: string[] };
  goalType: NodeType; // typically "bone"
  difficulty: Difficulty;
}

type Difficulty = "easy" | "medium" | "hard";

// Helpers
const NODE_TYPES: NodeType[] = ["bone", "flower", "carrot", "well", "blank"];

function randInt(n: number) {
  return Math.floor(Math.random() * n);
}

function pickOne<T>(arr: T[]): T {
  return arr[randInt(arr.length)];
}

function uid(prefix = "id"): string {
  return `${prefix}-${Math.random().toString(36).slice(2, 8)}`;
}

function serializeState(dog: string, bunnies: string[]) {
  const sorted = [...bunnies].sort();
  return `${dog}|${sorted.join(',')}`;
}

function conditionsSatisfied(conds: EdgeCond[], nodesById: Record<string, Node>, entitiesAtType: (t: NodeType) => number) {
  return conds.every((c) => {
    const count = entitiesAtType(c.type);
    if (c.kind === "somebodyAt") return count > 0;
    if (c.kind === "nobodyAt") return count === 0;
    return true;
  });
}

function layoutCircle(n: number, radius = 220) {
  const angleStep = (2 * Math.PI) / n;
  const centerX = 360;
  const centerY = 300;
  return Array.from({ length: n }).map((_, i) => ({
    x: centerX + radius * Math.cos(i * angleStep - Math.PI / 2),
    y: centerY + radius * Math.sin(i * angleStep - Math.PI / 2),
  }));
}

function genPuzzle(difficulty: Difficulty): PuzzleSpec {
  const params = {
    easy: { nNodes: 7, nBunnies: 2, outDeg: [1, 2], condsPerEdge: [1, 1], maxSteps: 12 },
    medium: { nNodes: 9, nBunnies: 3, outDeg: [1, 3], condsPerEdge: [1, 2], maxSteps: 20 },
    hard: { nNodes: 11, nBunnies: 4, outDeg: [2, 3], condsPerEdge: [2, 3], maxSteps: 30 },
  }[difficulty];

  // Nodes
  const positions = layoutCircle(params.nNodes);
  const nodes: Node[] = positions.map((p, i) => ({
    id: `n${i}`,
    type: pickOne(NODE_TYPES),
    x: p.x,
    y: p.y,
  }));
  // ensure at least one bone node
  if (!nodes.some((n) => n.type === "bone")) nodes[randInt(nodes.length)].type = "bone";

  // Edges
  const edges: Edge[] = [];
  for (let i = 0; i < nodes.length; i++) {
    const outMin = params.outDeg[0];
    const outMax = params.outDeg[1];
    const outCount = outMin + randInt(outMax - outMin + 1);
    const targets = new Set<number>();
    while (targets.size < outCount) {
      const t = randInt(nodes.length);
      if (t !== i) targets.add(t);
    }
    [...targets].forEach((t) => {
      const condCount = params.condsPerEdge[0] + randInt(params.condsPerEdge[1] - params.condsPerEdge[0] + 1);
      const conditions: EdgeCond[] = [];
      for (let k = 0; k < condCount; k++) {
        const kind = Math.random() < 0.6 ? "somebodyAt" : "nobodyAt";
        const type = pickOne(NODE_TYPES);
        conditions.push({ kind, type });
      }
      edges.push({ id: uid("e"), from: nodes[i].id, to: nodes[t].id, conditions });
    });
  }

  // Start positions
  const boneNodes = nodes.filter((n) => n.type === "bone");
  const startDog = pickOne(nodes).id;
  const bunnies: string[] = [];
  for (let i = 0; i < params.nBunnies; i++) bunnies.push(pickOne(nodes).id);

  return {
    nodes,
    edges,
    start: { dog: startDog, bunnies },
    goalType: "bone",
    difficulty,
  };
}

function solve(spec: PuzzleSpec, maxSteps: number): string[] | null {
  // BFS state space search to get dog to any node of goalType
  const nodesById = Object.fromEntries(spec.nodes.map((n) => [n.id, n] as const));
  const outEdges = new Map<string, Edge[]>();
  spec.edges.forEach((e) => {
    if (!outEdges.has(e.from)) outEdges.set(e.from, []);
    outEdges.get(e.from)!.push(e);
  });

  const start = { dog: spec.start.dog, bunnies: [...spec.start.bunnies] };
  const startKey = serializeState(start.dog, start.bunnies);
  const q: { dog: string; bunnies: string[]; path: string[] }[] = [{ ...start, path: [] }];
  const seen = new Set([startKey]);

  const entitiesAtType = (dog: string, bunnies: string[]) => (t: NodeType) => {
    let count = 0;
    if (nodesById[dog].type === t) count++;
    bunnies.forEach((b) => {
      if (nodesById[b].type === t) count++;
    });
    return count;
  };

  for (let depth = 0; depth <= maxSteps && q.length; depth++) {
    const cur = q.shift();
    if (!cur) break;
    const dogAtGoal = nodesById[cur.dog].type === spec.goalType;
    if (dogAtGoal) return cur.path;

    const entAt = entitiesAtType(cur.dog, cur.bunnies);

    // Moves: try moving dog and each bunny along available edges
    const candidates: Array<{ who: "dog" | number; from: string }> = [
      { who: "dog", from: cur.dog },
      ...cur.bunnies.map((id, idx) => ({ who: idx, from: id })),
    ];
    for (const c of candidates) {
      const outs = outEdges.get(c.from) || [];
      for (const e of outs) {
        if (!conditionsSatisfied(e.conditions, nodesById, entAt)) continue;
        const nextDog = c.who === "dog" ? e.to : cur.dog;
        const nextBunnies = cur.bunnies.map((b, i) => (c.who === i ? e.to : b));
        const key = serializeState(nextDog, nextBunnies);
        if (seen.has(key)) continue;
        seen.add(key);
        q.push({ dog: nextDog, bunnies: nextBunnies, path: [...cur.path, `${c.who === "dog" ? "Dog" : `Bunny ${c.who + 1}`}: ${c.from}→${e.to}`] });
      }
    }
  }
  return null;
}

function ensureSolvable(difficulty: Difficulty): PuzzleSpec {
  const limits = { easy: 12, medium: 20, hard: 30 } as const;
  for (let attempts = 0; attempts < 40; attempts++) {
    const candidate = genPuzzle(difficulty);
    const sol = solve(candidate, limits[difficulty]);
    if (sol) return candidate;
  }
  // Fallback — even if unsolved after attempts, return a simple hand-crafted tiny solvable graph
  const nodes: Node[] = layoutCircle(6).map((p, i) => ({ id: `n${i}`, type: i === 3 ? "bone" : pickOne(NODE_TYPES), x: p.x, y: p.y }));
  nodes[3].type = "bone";
  const edges: Edge[] = [
    { id: uid("e"), from: "n0", to: "n1", conditions: [{ kind: "somebodyAt", type: "flower" }] },
    { id: uid("e"), from: "n1", to: "n2", conditions: [{ kind: "somebodyAt", type: "carrot" }] },
    { id: uid("e"), from: "n2", to: "n3", conditions: [{ kind: "nobodyAt", type: "well" }] },
    { id: uid("e"), from: "n4", to: "n3", conditions: [{ kind: "somebodyAt", type: "carrot" }] },
  ];
  return { nodes, edges, start: { dog: "n0", bunnies: ["n4"] }, goalType: "bone", difficulty };
}

function encodeSpec(spec: PuzzleSpec) {
  return compressToEncodedURIComponent(JSON.stringify(spec));
}
function decodeSpec(s: string): PuzzleSpec | null {
  try {
    const txt = decompressFromEncodedURIComponent(s);
    if (!txt) return null;
    return JSON.parse(txt) as PuzzleSpec;
  } catch {
    return null;
  }
}

// Icons for node types (simple emoji for now to keep it lightweight)
const ICON: Record<NodeType, string> = {
  bone: "🦴",
  carrot: "🥕",
  flower: "🌸",
  well: "🪣",
  blank: "⬤",
};

// Component
const DogsBunnyPuzzle: React.FC = () => {
  const { toast } = useToast();
  const containerRef = useRef<HTMLDivElement | null>(null);

  const urlParam = new URLSearchParams(typeof window !== "undefined" ? window.location.search : "");
  const paramPuzzle = urlParam.get("p");
  const urlDifficulty = (urlParam.get("d") as Difficulty) || undefined;

  const [difficulty, setDifficulty] = useState<Difficulty>(urlDifficulty || "easy");
  const [spec, setSpec] = useState<PuzzleSpec>(() => {
    const decoded = paramPuzzle ? decodeSpec(paramPuzzle) : null;
    return decoded || ensureSolvable(urlDifficulty || "easy");
  });

  const nodesById = useMemo(() => Object.fromEntries(spec.nodes.map((n) => [n.id, n] as const)), [spec.nodes]);
  const outEdges = useMemo(() => {
    const m = new Map<string, Edge[]>();
    spec.edges.forEach((e) => {
      if (!m.has(e.from)) m.set(e.from, []);
      m.get(e.from)!.push(e);
    });
    return m;
  }, [spec.edges]);

  const [dog, setDog] = useState(spec.start.dog);
  const [bunnies, setBunnies] = useState<string[]>(spec.start.bunnies);
  const [selected, setSelected] = useState<{ who: "dog" | number; nodeId: string } | null>(null);
  const [solution, setSolution] = useState<string[] | null>(null);

  useEffect(() => {
    document.title = "Dogs & Bunny Puzzle – Interactive";
    const metaDesc = document.querySelector('meta[name="description"]');
    if (metaDesc) metaDesc.setAttribute("content", "Play the Dogs & Bunny puzzle. Generate random solvable puzzles, share via URL, and export screenshots.");
  }, []);

  const entAtType = useCallback(
    (t: NodeType) => {
      let count = nodesById[dog].type === t ? 1 : 0;
      bunnies.forEach((b) => {
        if (nodesById[b].type === t) count++;
      });
      return count;
    },
    [dog, bunnies, nodesById]
  );

  const availableMoves = useMemo(() => {
    if (!selected) return [] as { to: string; e: Edge }[];
    const outs = outEdges.get(selected.nodeId) || [];
    return outs
      .filter((e) => conditionsSatisfied(e.conditions, nodesById, entAtType))
      .map((e) => ({ to: e.to, e }));
  }, [selected, outEdges, entAtType, nodesById]);

  const doMove = (to: string) => {
    if (!selected) return;
    if (selected.who === "dog") setDog(to);
    else setBunnies((bb) => bb.map((b, i) => (i === selected.who ? to : b)));
    setSelected(null);
  };

  const reset = () => {
    setDog(spec.start.dog);
    setBunnies(spec.start.bunnies);
    setSelected(null);
    setSolution(null);
  };

  const regenerate = (d: Difficulty) => {
    const next = ensureSolvable(d);
    setSpec(next);
    setDog(next.start.dog);
    setBunnies(next.start.bunnies);
    setSelected(null);
    setSolution(null);
    setDifficulty(d);
    // update URL with encoded puzzle for shareability
    const params = new URLSearchParams(window.location.search);
    params.set("p", encodeSpec(next));
    params.set("d", d);
    const newUrl = `${window.location.pathname}?${params.toString()}`;
    window.history.replaceState({}, "", newUrl);
  };

  const copyLink = async () => {
    try {
      const encoded = encodeSpec({ ...spec, start: { dog, bunnies } });
      const url = new URL(window.location.href);
      url.searchParams.set("p", encoded);
      url.searchParams.set("d", difficulty);
      await navigator.clipboard.writeText(url.toString());
      toast({ title: "Link copied", description: "Share this exact puzzle and current state." });
    } catch (e) {
      toast({ title: "Copy failed", description: "Could not copy link.", variant: "destructive" });
    }
  };

  const exportPng = async () => {
    if (!containerRef.current) return;
    try {
      const dataUrl = await htmlToImage.toPng(containerRef.current, { pixelRatio: 2, cacheBust: true, backgroundColor: "transparent" });
      saveAs(dataUrl, "dogs-bunny-puzzle.png");
    } catch (e) {
      toast({ title: "Export failed", description: "Please try again.", variant: "destructive" });
    }
  };

  const computeSolution = () => {
    const sol = solve({ ...spec, start: { dog, bunnies } }, { easy: 12, medium: 20, hard: 30 }[spec.difficulty]);
    setSolution(sol);
    if (!sol) toast({ title: "No solution found", description: "This state might be a dead end." });
  };

  // Render helpers
  const size = { w: 720, h: 520 };

  const nodeEl = (n: Node) => {
    const isSelected = selected?.nodeId === n.id;
    const isDog = dog === n.id;
    const bunnyCount = bunnies.filter((b) => b === n.id).length;
    const onSelect = () => {
      if (isDog) setSelected({ who: "dog", nodeId: n.id });
      else if (bunnyCount > 0) {
        // pick the first bunny at this node (simple UX)
        const idx = bunnies.findIndex((b) => b === n.id);
        setSelected({ who: idx as number, nodeId: n.id });
      } else setSelected(null);
    };
    const atGoal = nodesById[dog].type === spec.goalType && n.id === dog;

    return (
      <g key={n.id} onClick={onSelect} className="cursor-pointer">
        <circle cx={n.x} cy={n.y} r={32} className={cn("fill-background stroke-border", isSelected && "ring-2 ring-primary fill-accent")} />
        <text x={n.x} y={n.y - 20} textAnchor="middle" className="fill-muted-foreground text-[10px] select-none">
          {n.type}
        </text>
        <text x={n.x} y={n.y + 4} textAnchor="middle" className="text-xl select-none">
          {ICON[n.type]}
        </text>
        {isDog && (
          <text x={n.x - 18} y={n.y + 26} textAnchor="middle" className="select-none">🐶</text>
        )}
        {bunnyCount > 0 && (
          <text x={n.x + 10} y={n.y + 26} textAnchor="middle" className="select-none">{"🐰".repeat(Math.min(3, bunnyCount))}</text>
        )}
        {atGoal && (
          <text x={n.x} y={n.y - 38} textAnchor="middle" className="fill-primary text-xs">Goal!</text>
        )}
      </g>
    );
  };

  const edgeEl = (e: Edge) => {
    const a = nodesById[e.from];
    const b = nodesById[e.to];
    const midx = (a.x + b.x) / 2;
    const midy = (a.y + b.y) / 2;

    // arrow line
    const dx = b.x - a.x;
    const dy = b.y - a.y;
    const len = Math.hypot(dx, dy) || 1;
    const ux = dx / len;
    const uy = dy / len;
    const endx = b.x - ux * 34;
    const endy = b.y - uy * 34;

    const active = conditionsSatisfied(e.conditions, nodesById, entAtType);

    return (
      <g key={e.id}>
        <line x1={a.x} y1={a.y} x2={endx} y2={endy} className={cn("stroke-border", active ? "stroke-primary" : "opacity-60")} markerEnd="url(#arrow)" />
        <foreignObject x={midx - 70} y={midy - 14} width={140} height={28}>
          <div className={cn("px-1 py-0.5 text-[10px] rounded-md border", active ? "border-primary text-foreground" : "border-muted text-muted-foreground opacity-70")}
               title={e.conditions.map((c) => `${c.kind === "somebodyAt" ? "somebody" : "nobody"} at ${c.type}`).join(" AND ")}>
            {e.conditions.map((c, i) => (
              <span key={i} className="mr-1">
                {c.kind === "somebodyAt" ? "∃" : "∄"} {ICON[c.type]}
                {i < e.conditions.length - 1 ? " ∧" : ""}
              </span>
            ))}
          </div>
        </foreignObject>
      </g>
    );
  };

  const puzzleEncoded = useMemo(() => encodeSpec(spec), [spec]);
  const shareUrl = useMemo(() => {
    const u = new URL(window.location.href);
    u.searchParams.set("p", puzzleEncoded);
    u.searchParams.set("d", spec.difficulty);
    return u.toString();
  }, [puzzleEncoded, spec.difficulty]);

  return (
    <section className="space-y-4">
      <header className="flex items-center justify-between gap-2">
        <h1 className="text-2xl font-bold">Dogs & Bunny Puzzle</h1>
        <div className="flex items-center gap-2">
          <select
            aria-label="Difficulty"
            className="h-10 rounded-md border bg-background px-3"
            value={difficulty}
            onChange={(e) => regenerate(e.target.value as Difficulty)}
          >
            <option value="easy">Easy</option>
            <option value="medium">Medium</option>
            <option value="hard">Hard</option>
          </select>
          <Button variant="secondary" onClick={() => regenerate(difficulty)} title="New puzzle"><Shuffle className="mr-1" />New</Button>
          <Button variant="outline" onClick={reset} title="Reset">Reset</Button>
          <Button variant="outline" onClick={computeSolution} title="Try solve">Solve</Button>
          <Button variant="outline" onClick={copyLink} title="Copy shareable link"><Copy className="mr-1" />Link</Button>
          <Button variant="default" onClick={exportPng} title="Export PNG"><Download className="mr-1" />PNG</Button>
        </div>
      </header>

      <Card className="p-2" ref={containerRef}>
        <svg width={size.w} height={size.h} viewBox={`0 0 ${size.w} ${size.h}`}>
          <defs>
            <marker id="arrow" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
              <path d="M 0 0 L 10 5 L 0 10 z" className="fill-muted-foreground" />
            </marker>
          </defs>
          {/* edges first */}
          {spec.edges.map(edgeEl)}
          {/* nodes on top */}
          {spec.nodes.map(nodeEl)}
        </svg>
      </Card>

      <div className="grid md:grid-cols-2 gap-4">
        <Card className="p-4">
          <h2 className="font-semibold mb-2">How to Play</h2>
          <ul className="list-disc pl-5 text-sm text-muted-foreground space-y-1">
            <li>Click the dog or a bunny to see available moves; click a destination node to move along active edges.</li>
            <li>Edges are active only if all shown conditions are currently true.</li>
            <li>Goal: move the dog to any bone node.</li>
          </ul>
        </Card>
        <Card className="p-4">
          <h2 className="font-semibold mb-2">Share & Export</h2>
          <p className="text-sm text-muted-foreground mb-2">Copy a link to share the exact puzzle. Export a PNG snapshot of the puzzle or your current solution.</p>
          <SocialShare title="Dogs & Bunny Puzzle" description="A cute logic puzzle with dependencies" url={shareUrl} />
        </Card>
      </div>

      {solution && (
        <Card className="p-4">
          <h2 className="font-semibold mb-2">Solver Output</h2>
          {solution ? (
            <ol className="list-decimal pl-5 text-sm text-muted-foreground space-y-1">
              {solution.map((s, i) => (
                <li key={i}>{s}</li>
              ))}
            </ol>
          ) : (
            <p className="text-sm text-muted-foreground">No solution found.</p>
          )}
        </Card>
      )}
    </section>
  );
};

export default DogsBunnyPuzzle;
