import React, { useState, useEffect, useRef, useCallback } from 'react';
import ReactDOM from 'react-dom/client';

// --- TYPES ---

enum GameMode {
  SINGLE = 'SINGLE',
  MULTI = 'MULTI',
}

enum GameState {
  MENU = 'MENU',
  PLAYING = 'PLAYING',
  GAME_OVER = 'GAME_OVER',
  LEADERBOARD = 'LEADERBOARD',
}

interface Player {
  score: number;
  y: number;
  height: number;
  color: string;
}

interface Ball {
  x: number;
  y: number;
  dx: number;
  dy: number;
  size: number;
  color: string;
}

interface LeaderboardEntry {
  name: string;
  time: number;
  date: string;
}

interface GameSettings {
  winningScore: number;
  paddleSpeed: number;
  initialBallSpeed: number;
  rallyThreshold: number;
  powerUpDuration: number;
}

// --- CONSTANTS ---

const CANVAS_WIDTH = 800;
const CANVAS_HEIGHT = 600;
const PADDLE_WIDTH = 15;
const INITIAL_PADDLE_HEIGHT = 100;
const POWERUP_PADDLE_HEIGHT = 133;

const SETTINGS: GameSettings = {
  winningScore: 10,
  paddleSpeed: 8,
  initialBallSpeed: 6,
  rallyThreshold: 23,
  powerUpDuration: 15,
};

const STORAGE_KEY = 'pong_leaderboard_capoli';

const getRandomColor = (): string => {
  const h = Math.floor(Math.random() * 360);
  const s = Math.floor(Math.random() * 20) + 80;
  const l = Math.floor(Math.random() * 20) + 50;
  return `hsl(${h}, ${s}%, ${l}%)`;
};

// --- UTILS ---

const getLeaderboard = (): LeaderboardEntry[] => {
  try {
    const data = localStorage.getItem(STORAGE_KEY);
    return data ? JSON.parse(data) : [];
  } catch (e) {
    console.error("Failed to load leaderboard", e);
    return [];
  }
};

const saveScoreToStorage = (name: string, time: number) => {
  const currentLeaderboard = getLeaderboard();
  
  const newEntry: LeaderboardEntry = {
    name: name.substring(0, 8),
    time,
    date: new Date().toLocaleDateString('en-US'),
  };

  const updatedLeaderboard = [...currentLeaderboard, newEntry]
    .sort((a, b) => a.time - b.time)
    .slice(0, 20);

  localStorage.setItem(STORAGE_KEY, JSON.stringify(updatedLeaderboard));
};

// --- COMPONENTS ---

// Button Component
const Button: React.FC<React.ButtonHTMLAttributes<HTMLButtonElement> & { variant?: 'primary' | 'secondary' }> = ({ children, variant = 'primary', className = '', ...props }) => {
  const baseStyle = "px-6 py-3 rounded text-lg font-bold transition-transform active:scale-95 font-arcade border-2";
  const variants = {
    primary: "bg-white text-black border-white hover:bg-gray-200",
    secondary: "bg-black text-white border-white hover:bg-gray-900",
  };

  return (
    <button 
      className={`${baseStyle} ${variants[variant]} ${className}`} 
      {...props}
    >
      {children}
    </button>
  );
};

// GameCanvas Component
interface GameCanvasProps {
  mode: GameMode;
  onGameOver: (winner: 1 | 2, finalTime: number) => void;
}

const GameCanvas: React.FC<GameCanvasProps> = ({ mode, onGameOver }) => {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const requestRef = useRef<number>(0);
  const startTimeRef = useRef<number>(Date.now());
  
  const p1Ref = useRef<Player>({ score: 0, y: CANVAS_HEIGHT / 2 - INITIAL_PADDLE_HEIGHT / 2, height: INITIAL_PADDLE_HEIGHT, color: '#FFFFFF' });
  const p2Ref = useRef<Player>({ score: 0, y: CANVAS_HEIGHT / 2 - INITIAL_PADDLE_HEIGHT / 2, height: INITIAL_PADDLE_HEIGHT, color: '#FFFFFF' });
  const ballRef = useRef<Ball>({ 
    x: CANVAS_WIDTH / 2, 
    y: CANVAS_HEIGHT / 2, 
    dx: SETTINGS.initialBallSpeed * (Math.random() > 0.5 ? 1 : -1), 
    dy: SETTINGS.initialBallSpeed * (Math.random() > 0.5 ? 1 : -1), 
    size: 10, 
    color: '#FFFFFF' 
  });
  
  const keysPressed = useRef<{ [key: string]: boolean }>({});
  const rallyCount = useRef<number>(0);
  const powerUpHitsRemaining = useRef<number>(0);

  useEffect(() => {
    const handleKeyDown = (e: KeyboardEvent) => { keysPressed.current[e.key.toLowerCase()] = true; };
    const handleKeyUp = (e: KeyboardEvent) => { keysPressed.current[e.key.toLowerCase()] = false; };
    
    const handleKeyScroll = (e: KeyboardEvent) => {
        if(["ArrowUp","ArrowDown"," "].indexOf(e.code) > -1) {
            e.preventDefault();
        }
    };

    window.addEventListener('keydown', handleKeyDown);
    window.addEventListener('keyup', handleKeyUp);
    window.addEventListener('keydown', handleKeyScroll, false);

    return () => {
      window.removeEventListener('keydown', handleKeyDown);
      window.removeEventListener('keyup', handleKeyUp);
      window.removeEventListener('keydown', handleKeyScroll);
    };
  }, []);

  const resetBall = useCallback((scorer: 1 | 2) => {
    ballRef.current = {
      x: CANVAS_WIDTH / 2,
      y: CANVAS_HEIGHT / 2,
      dx: SETTINGS.initialBallSpeed * (scorer === 1 ? -1 : 1),
      dy: (Math.random() * 4 - 2) + (Math.random() > 0.5 ? 2 : -2),
      size: 10,
      color: '#FFFFFF'
    };
    p1Ref.current.color = '#FFFFFF';
    p2Ref.current.color = '#FFFFFF';
    rallyCount.current = 0;
    powerUpHitsRemaining.current = 0;
    p1Ref.current.height = INITIAL_PADDLE_HEIGHT;
    p2Ref.current.height = INITIAL_PADDLE_HEIGHT;
  }, []);

  const changeColors = () => {
    const newColor = getRandomColor();
    ballRef.current.color = newColor;
    p1Ref.current.color = newColor;
    p2Ref.current.color = newColor;
  };

  const checkPowerUp = () => {
    if (powerUpHitsRemaining.current > 0) {
      powerUpHitsRemaining.current--;
      if (powerUpHitsRemaining.current === 0) {
        p1Ref.current.height = INITIAL_PADDLE_HEIGHT;
        p2Ref.current.height = INITIAL_PADDLE_HEIGHT;
      }
    } else {
      if (rallyCount.current >= SETTINGS.rallyThreshold) {
         p1Ref.current.height = POWERUP_PADDLE_HEIGHT;
         p2Ref.current.height = POWERUP_PADDLE_HEIGHT;
         powerUpHitsRemaining.current = SETTINGS.powerUpDuration;
      }
    }
  };

  const update = () => {
    const p1 = p1Ref.current;
    const p2 = p2Ref.current;
    const ball = ballRef.current;

    // Player 1 Movement
    if (mode === GameMode.MULTI) {
      if (keysPressed.current['w']) p1.y -= SETTINGS.paddleSpeed;
      if (keysPressed.current['s']) p1.y += SETTINGS.paddleSpeed;
    } else {
      // Single Player: Controlled by arrows
      if (keysPressed.current['arrowup']) p1.y -= SETTINGS.paddleSpeed;
      if (keysPressed.current['arrowdown']) p1.y += SETTINGS.paddleSpeed;
    }

    // Player 2 Movement
    if (mode === GameMode.MULTI) {
      if (keysPressed.current['arrowup']) p2.y -= SETTINGS.paddleSpeed;
      if (keysPressed.current['arrowdown']) p2.y += SETTINGS.paddleSpeed;
    } else {
      // AI
      const centerP2 = p2.y + p2.height / 2;
      if (ball.dx > 0) {
        if (centerP2 < ball.y - 10) p2.y += SETTINGS.paddleSpeed * 0.85;
        else if (centerP2 > ball.y + 10) p2.y -= SETTINGS.paddleSpeed * 0.85;
      }
    }

    p1.y = Math.max(0, Math.min(CANVAS_HEIGHT - p1.height, p1.y));
    p2.y = Math.max(0, Math.min(CANVAS_HEIGHT - p2.height, p2.y));

    ball.x += ball.dx;
    ball.y += ball.dy;

    if (ball.y <= 0 || ball.y + ball.size >= CANVAS_HEIGHT) {
      ball.dy *= -1;
    }

    // P1 Collision
    if (
      ball.x <= PADDLE_WIDTH + 10 && 
      ball.x >= PADDLE_WIDTH - 10 && 
      ball.y + ball.size >= p1.y && 
      ball.y <= p1.y + p1.height
    ) {
      ball.dx = Math.abs(ball.dx);
      ball.dx *= 1.025; // 2.5% increase
      ball.dy *= 1.025;
      
      rallyCount.current++;
      changeColors();
      checkPowerUp();
    }

    // P2 Collision
    if (
      ball.x + ball.size >= CANVAS_WIDTH - PADDLE_WIDTH - 10 &&
      ball.x + ball.size <= CANVAS_WIDTH - PADDLE_WIDTH + 10 &&
      ball.y + ball.size >= p2.y && 
      ball.y <= p2.y + p2.height
    ) {
      ball.dx = -Math.abs(ball.dx);
      ball.dx *= 1.025; // 2.5% increase
      ball.dy *= 1.025;

      rallyCount.current++;
      changeColors();
      checkPowerUp();
    }

    // Scoring
    if (ball.x < 0) {
      p2.score++;
      if (p2.score >= SETTINGS.winningScore) {
        const duration = (Date.now() - startTimeRef.current) / 1000;
        onGameOver(2, duration);
      } else {
        resetBall(2);
      }
    } else if (ball.x > CANVAS_WIDTH) {
      p1.score++;
      if (p1.score >= SETTINGS.winningScore) {
        const duration = (Date.now() - startTimeRef.current) / 1000;
        onGameOver(1, duration);
      } else {
        resetBall(1);
      }
    }
  };

  const draw = (ctx: CanvasRenderingContext2D) => {
    ctx.fillStyle = '#000000';
    ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);

    ctx.strokeStyle = '#333';
    ctx.setLineDash([10, 15]);
    ctx.beginPath();
    ctx.moveTo(CANVAS_WIDTH / 2, 0);
    ctx.lineTo(CANVAS_WIDTH / 2, CANVAS_HEIGHT);
    ctx.stroke();
    ctx.setLineDash([]);

    ctx.font = '40px "Press Start 2P"';
    ctx.fillStyle = '#666';
    ctx.fillText(p1Ref.current.score.toString(), CANVAS_WIDTH / 4, 80);
    ctx.fillText(p2Ref.current.score.toString(), (CANVAS_WIDTH / 4) * 3, 80);

    const elapsed = Math.floor((Date.now() - startTimeRef.current) / 1000);
    ctx.font = '16px "Roboto"';
    ctx.fillStyle = '#888';
    ctx.fillText(`TIME: ${elapsed}s`, CANVAS_WIDTH / 2 - 40, 30);
    ctx.fillText(`RALLY: ${rallyCount.current}`, CANVAS_WIDTH / 2 - 40, 50);

    ctx.fillStyle = p1Ref.current.color;
    ctx.fillRect(10, p1Ref.current.y, PADDLE_WIDTH, p1Ref.current.height);

    ctx.fillStyle = p2Ref.current.color;
    ctx.fillRect(CANVAS_WIDTH - PADDLE_WIDTH - 10, p2Ref.current.y, PADDLE_WIDTH, p2Ref.current.height);

    ctx.fillStyle = ballRef.current.color;
    ctx.fillRect(ballRef.current.x, ballRef.current.y, ballRef.current.size, ballRef.current.size);
  };

  const loop = () => {
    update();
    const ctx = canvasRef.current?.getContext('2d');
    if (ctx) {
      draw(ctx);
    }
    requestRef.current = requestAnimationFrame(loop);
  };

  useEffect(() => {
    requestRef.current = requestAnimationFrame(loop);
    return () => cancelAnimationFrame(requestRef.current);
  }, []);

  return (
    <div className="relative border-4 border-gray-800 rounded-lg shadow-[0_0_20px_rgba(255,255,255,0.1)]">
      <canvas 
        ref={canvasRef} 
        width={CANVAS_WIDTH} 
        height={CANVAS_HEIGHT}
        className="block bg-black"
        style={{ maxWidth: '100%', maxHeight: '80vh' }}
      />
    </div>
  );
};

// Leaderboard Component
interface LeaderboardProps {
  onBack: () => void;
}

const Leaderboard: React.FC<LeaderboardProps> = ({ onBack }) => {
  const scores = getLeaderboard();

  return (
    <div className="flex flex-col items-center justify-center w-full max-w-2xl bg-black border-2 border-white p-8 rounded-lg shadow-2xl">
      <h2 className="text-3xl font-arcade text-yellow-400 mb-6 tracking-wider">LEADERBOARD</h2>
      
      <div className="w-full mb-8 overflow-y-auto max-h-[500px]">
        <table className="w-full text-left border-collapse">
          <thead>
            <tr className="border-b-2 border-gray-700 text-gray-400">
              <th className="p-3 font-mono">#</th>
              <th className="p-3 font-mono">NAME</th>
              <th className="p-3 font-mono text-right">TIME (s)</th>
              <th className="p-3 font-mono text-right">DATE</th>
            </tr>
          </thead>
          <tbody>
            {scores.length === 0 ? (
              <tr>
                <td colSpan={4} className="text-center p-8 text-gray-600 italic">
                  No records yet. Be the first!
                </td>
              </tr>
            ) : (
              scores.map((entry, index) => (
                <tr key={index} className="border-b border-gray-900 hover:bg-gray-900 transition-colors">
                  <td className="p-3 text-yellow-500 font-bold">{index + 1}.</td>
                  <td className="p-3 text-white uppercase tracking-widest">{entry.name}</td>
                  <td className="p-3 text-right text-green-400 font-mono">{entry.time.toFixed(1)}</td>
                  <td className="p-3 text-right text-gray-500 text-sm">{entry.date}</td>
                </tr>
              ))
            )}
          </tbody>
        </table>
      </div>

      <Button onClick={onBack} variant="primary">BACK TO MENU</Button>
    </div>
  );
};

// GameOverModal Component
interface GameOverModalProps {
  winner: 1 | 2;
  time: number;
  mode: GameMode;
  onSave: (name: string) => void;
  onRestart: () => void;
}

const GameOverModal: React.FC<GameOverModalProps> = ({ winner, time, mode, onSave, onRestart }) => {
  const [name, setName] = useState('');
  const [error, setError] = useState('');

  const isSinglePlayer = mode === GameMode.SINGLE;
  // Save only if user won in single player, or always in multiplayer
  const canSave = !isSinglePlayer || winner === 1;

  let winnerDisplay = `PLAYER ${winner}`;
  if (isSinglePlayer) {
    winnerDisplay = winner === 1 ? 'YOU' : 'CPU';
  }

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    if (!name.trim()) {
      setError('Enter name');
      return;
    }
    if (name.length > 8) {
      setError('Max 8 chars');
      return;
    }
    onSave(name);
  };

  return (
    <div className="absolute inset-0 flex items-center justify-center bg-black/80 backdrop-blur-sm z-50">
      <div className="bg-black border-2 border-white p-8 rounded-lg text-center max-w-md w-full animate-bounce-in shadow-[0_0_30px_rgba(255,255,255,0.3)]">
        <h2 className="text-4xl font-arcade text-yellow-400 mb-2">GAME OVER</h2>
        <p className="text-2xl text-white mb-6">
          WINNER: <span className="text-green-400">{winnerDisplay}</span>
        </p>
        <p className="text-lg text-gray-400 mb-8 font-mono">
          TIME: {time.toFixed(2)} seconds
        </p>

        {canSave ? (
          <form onSubmit={handleSubmit} className="mb-8">
            <div className="flex flex-col gap-2">
              <label className="text-sm text-gray-500 uppercase">
                ENTER {isSinglePlayer ? 'YOUR' : 'WINNER'} NAME (MAX 8 CHARS)
              </label>
              <input 
                type="text" 
                value={name}
                onChange={(e) => {
                  setName(e.target.value.toUpperCase());
                  setError('');
                }}
                maxLength={8}
                className="bg-gray-900 border-b-2 border-white text-center text-2xl p-2 text-white focus:outline-none focus:border-yellow-400 font-mono tracking-widest uppercase"
                placeholder={isSinglePlayer ? "PLAYER" : `PLAYER ${winner}`}
                autoFocus
              />
              {error && <p className="text-red-500 text-xs">{error}</p>}
            </div>
            <Button type="submit" className="mt-4 w-full" variant="primary">SAVE SCORE</Button>
          </form>
        ) : (
          <div className="mb-8 text-gray-400 font-mono">
            Better luck next time!
          </div>
        )}

        <Button onClick={onRestart} variant="secondary" className="w-full">PLAY AGAIN</Button>
      </div>
    </div>
  );
};

// --- APP ---

const App: React.FC = () => {
  const [gameState, setGameState] = useState<GameState>(GameState.MENU);
  const [mode, setMode] = useState<GameMode>(GameMode.SINGLE);
  const [lastGameResult, setLastGameResult] = useState<{ winner: 1 | 2; time: number } | null>(null);

  const startGame = (selectedMode: GameMode) => {
    setMode(selectedMode);
    setGameState(GameState.PLAYING);
  };

  const handleGameOver = (winner: 1 | 2, time: number) => {
    setLastGameResult({ winner, time });
    setGameState(GameState.GAME_OVER);
  };

  const handleSaveScore = (name: string) => {
    if (lastGameResult) {
      saveScoreToStorage(name, lastGameResult.time);
      setGameState(GameState.LEADERBOARD);
    }
  };

  return (
    <div className="min-h-screen bg-black flex flex-col items-center justify-center p-4">
      {gameState === GameState.MENU && (
        <div className="text-center animate-fade-in">
          <h1 className="text-6xl md:text-8xl font-arcade text-white mb-12 tracking-tighter neon-text" style={{ textShadow: '0 0 20px #fff, 0 0 30px #ff00de' }}>
            PONG
          </h1>
          
          <div className="flex flex-col gap-6 w-64 mx-auto">
            <Button onClick={() => startGame(GameMode.SINGLE)}>SINGLE PLAYER</Button>
            <Button onClick={() => startGame(GameMode.MULTI)}>TWO PLAYERS</Button>
            <Button onClick={() => setGameState(GameState.LEADERBOARD)} variant="secondary">LEADERBOARD</Button>
          </div>

          <div className="mt-12 text-gray-600 text-sm max-w-md mx-auto">
            <p>1 Player Controls: Arrows Up/Down</p>
            <p>2 Players Controls: P1 (W/S) - P2 (Arrows)</p>
            <br />
            <a href="../cubes/index.html" title="Back"> BACK</a>
          </div>
        </div>
      )}

      {gameState === GameState.PLAYING && (
        <GameCanvas mode={mode} onGameOver={handleGameOver} />
      )}

      {gameState === GameState.GAME_OVER && lastGameResult && (
        <GameOverModal 
          winner={lastGameResult.winner} 
          time={lastGameResult.time} 
          mode={mode}
          onSave={handleSaveScore}
          onRestart={() => setGameState(GameState.MENU)}
        />
      )}

      {gameState === GameState.LEADERBOARD && (
        <Leaderboard onBack={() => setGameState(GameState.MENU)} />
      )}
    </div>
  );
};

// --- RENDER ---

const rootElement = document.getElementById('root');
if (!rootElement) {
  throw new Error("Could not find root element to mount to");
}

const root = ReactDOM.createRoot(rootElement);
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
