import React, { useState, useEffect, useRef, useCallback, useMemo } from "react";
import MonkeySprite from "../components/MonkeySprite";
import { getWsUrl } from "../config";
import "../App.css";

function Type() {
  const [letters, setLetters] = useState([]);
  const [selectedIndices, setSelectedIndices] = useState([]);
  const [connected, setConnected] = useState(false);
  const [message, setMessage] = useState("");
  const [monkeyAnimation, setMonkeyAnimation] = useState("idle");
  const [flyingLetters, setFlyingLetters] = useState([]);
  const selectedSet = useMemo(() => new Set(selectedIndices), [selectedIndices]);
  const isDraggingRef = useRef(false);
  const selectionStartRef = useRef(null);
  const letterBoxRef = useRef(null);
  const userScrolledRef = useRef(false);
  const typewriterRef = useRef(null);
  const flyingLetterIdRef = useRef(0);
  const pendingTimeoutsRef = useRef([]);

  useEffect(() => {
    let mounted = true;
    let ws = null;

    const connect = () => {
      if (!mounted) return;

      ws = new WebSocket(getWsUrl());

      ws.onopen = () => {
        if (!mounted) return;
        setConnected(true);
      };

      ws.onmessage = (event) => {
        if (!mounted) return;
        const data = JSON.parse(event.data);

        if (data.type === "init") {
          setLetters(data.letters);
        } else if (data.type === "letter") {
          setMonkeyAnimation("punch");

          // Delay everything to when the fist lands (~frame 2-3)
          const letterAtImpact = data.letter;
          const timestampAtImpact = data.timestamp;
          const impactTimeout = setTimeout(() => {
            setLetters((prev) => [
              ...prev,
              { letter: letterAtImpact, timestamp: timestampAtImpact },
            ]);

            if (typewriterRef.current) {
              typewriterRef.current.classList.remove("shaking");
              void typewriterRef.current.offsetWidth;
              typewriterRef.current.classList.add("shaking");
            }

            const id = flyingLetterIdRef.current++;
            const angle = Math.random() * Math.PI * 2;
            const distance = 50 + Math.random() * 60;
            const dx = Math.cos(angle) * distance;
            const dy = Math.sin(angle) * distance;
            setFlyingLetters((prev) => [
              ...prev,
              { id, letter: letterAtImpact, dx, dy },
            ]);
            const cleanupTimeout = setTimeout(() => {
              setFlyingLetters((prev) => prev.filter((l) => l.id !== id));
            }, 900);
            pendingTimeoutsRef.current.push(cleanupTimeout);
          }, 180);
          pendingTimeoutsRef.current.push(impactTimeout);
        }
      };

      ws.onclose = () => {
        if (!mounted) return;
        setConnected(false);
        setTimeout(connect, 3000);
      };
    };

    connect();

    return () => {
      mounted = false;
      if (ws) ws.close();
      pendingTimeoutsRef.current.forEach(clearTimeout);
      pendingTimeoutsRef.current = [];
    };
  }, []);

  // Auto-scroll to bottom when new letters arrive, unless user has scrolled up
  useEffect(() => {
    if (!userScrolledRef.current && letterBoxRef.current) {
      letterBoxRef.current.scrollTop = letterBoxRef.current.scrollHeight;
    }
  }, [letters.length]);

  const handleLetterBoxScroll = useCallback(() => {
    const el = letterBoxRef.current;
    if (!el) return;
    const atBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 30;
    userScrolledRef.current = !atBottom;
  }, []);

  const handlePunchEnd = useCallback(() => {
    setMonkeyAnimation("idle");
  }, []);

  const handleMouseDown = useCallback((index, e) => {
    e.preventDefault();
    isDraggingRef.current = true;
    selectionStartRef.current = index;
    setSelectedIndices([index]);
  }, []);

  const handleMouseEnter = useCallback((index) => {
    if (!isDraggingRef.current || selectionStartRef.current === null) return;

    const start = selectionStartRef.current;

    if (index >= start) {
      const newSelection = [];
      for (let i = start; i <= index; i++) {
        newSelection.push(i);
      }
      setSelectedIndices(newSelection);
    } else {
      const newSelection = [];
      for (let i = index; i <= start; i++) {
        newSelection.push(i);
      }
      setSelectedIndices(newSelection);
    }
  }, []);

  useEffect(() => {
    const handleGlobalMouseUp = () => {
      isDraggingRef.current = false;
    };

    document.addEventListener("mouseup", handleGlobalMouseUp);
    return () => {
      document.removeEventListener("mouseup", handleGlobalMouseUp);
    };
  }, []);

  const clearSelection = () => {
    setSelectedIndices([]);
    selectionStartRef.current = null;
    setMessage("");
  };

  const submitPhrase = async () => {
    if (selectedIndices.length === 0) return;

    const sortedIndices = [...selectedIndices].sort((a, b) => a - b);
    const selectedLetters = sortedIndices.map((i) => letters[i]);
    const phrase = selectedLetters.map((l) => l.letter).join("");
    const timestamps = selectedLetters.map((l) => l.timestamp);

    try {
      const response = await fetch("/api/submit", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ phrase, timestamps }),
      });

      const data = await response.json();

      if (response.ok) {
        setMessage("✓ Submitted successfully!");
        clearSelection();
        setTimeout(() => setMessage(""), 3000);
      } else {
        setMessage("✗ " + (data.error || "Failed to submit"));
      }
    } catch (error) {
      setMessage("✗ Network error");
    }
  };

  const selectedPhrase = useMemo(() =>
    selectedIndices.length > 0
      ? [...selectedIndices]
          .sort((a, b) => a - b)
          .map((i) => letters[i]?.letter || "")
          .join("")
      : "",
  [selectedIndices, letters]);

  return (
    <div
      style={{
        minHeight: "100vh",
        display: "flex",
        flexDirection: "column",
      }}
    >
      <div
        style={{
          padding: "20px",
          paddingTop: "73px",
          width: "100%",
          maxWidth: "700px",
          margin: "0 auto",
          flex: 1,
        }}
      >
        {/* Header row: title + status + monkey all on one line */}
        <div
          style={{
            display: "flex",
            alignItems: "center",
            justifyContent: "space-between",
            marginBottom: "20px",
            gap: "16px",
          }}
        >
          <div>
            <h1 style={{ margin: 0 }}>Type</h1>
            <span style={{ color: connected ? "#4caf50" : "#666", fontSize: "0.9em", marginTop: "10px", display: "block" }}>
              {connected ? "● Connected" : "○ Disconnected"}
            </span>
          </div>

          <div className="monkey-widget">
            <div className="typewriter-wrapper">
              <img
                ref={typewriterRef}
                src="/assets/Typewriter/typewriter.png"
                alt=""
                className="typewriter-img"
              />
              {flyingLetters.map(({ id, letter, dx, dy }) => (
                <span
                  key={id}
                  className="flying-letter"
                  style={{ "--dx": `${dx}px`, "--dy": `${dy}px` }}
                >
                  {letter}
                </span>
              ))}
            </div>
            <MonkeySprite
              animation={monkeyAnimation}
              onAnimationEnd={handlePunchEnd}
              className="monkey-img"
            />
          </div>
        </div>

        {/* Letter display area */}
        <div
          ref={letterBoxRef}
          onScroll={handleLetterBoxScroll}
          style={{
            border: "2px solid #444",
            padding: "20px",
            height: "300px",
            overflowY: "auto",
            overflowX: "hidden",
            userSelect: "none",
            backgroundColor: "#1a1a1a",
            color: "#f0f0f0",
            fontSize: "18px",
            lineHeight: "1.8",
            wordBreak: "break-all",
          }}
        >
          {letters.map((letter, index) => (
            <span
              key={`${index}-${letter.timestamp}`}
              onMouseDown={(e) => handleMouseDown(index, e)}
              onMouseEnter={() => handleMouseEnter(index)}
              style={{
                cursor: "pointer",
                backgroundColor: selectedSet.has(index)
                  ? "#fff"
                  : "transparent",
                color: selectedSet.has(index) ? "#111" : "#f0f0f0",
                padding: "2px 1px",
                display: "inline",
                transition: "background-color 0.1s",
              }}
            >
              {letter.letter}
            </span>
          ))}
        </div>

        {/* Control Panel */}
        <div style={{ marginTop: "20px" }}>
          <div
            style={{
              padding: "15px",
              backgroundColor: "#1a1a1a",
              border: "1px solid #444",
              minHeight: "50px",
              marginBottom: "15px",
              fontSize: "16px",
              color: "#ccc",
            }}
          >
            <strong style={{ color: "#f0f0f0" }}>Selected:</strong>{" "}
            {selectedPhrase || "(drag to select letters)"}
          </div>

          <div style={{ display: "flex", gap: "10px", flexWrap: "wrap" }}>
            <button
              onClick={clearSelection}
              style={{
                padding: "10px 20px",
                fontSize: "16px",
                cursor: "pointer",
                backgroundColor: "transparent",
                color: "#f0f0f0",
                border: "2px solid #555",
                borderRadius: "4px",
              }}
            >
              Clear
            </button>
            <button
              onClick={submitPhrase}
              disabled={selectedIndices.length === 0}
              style={{
                padding: "10px 20px",
                fontSize: "16px",
                cursor:
                  selectedIndices.length === 0 ? "not-allowed" : "pointer",
                backgroundColor:
                  selectedIndices.length === 0 ? "#333" : "#f0f0f0",
                color: selectedIndices.length === 0 ? "#666" : "#111",
                border: "2px solid #555",
                borderRadius: "4px",
                opacity: selectedIndices.length === 0 ? 0.5 : 1,
              }}
            >
              Submit to Leaderboard
            </button>
          </div>

          {message && (
            <div
              style={{
                marginTop: "15px",
                padding: "10px",
                backgroundColor: "#1a1a1a",
                border: `1px solid ${message.startsWith("✓") ? "#555" : "#444"}`,
                borderRadius: "4px",
                color: message.startsWith("✓") ? "#ccc" : "#888",
              }}
            >
              {message}
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

export default Type;
