import React, { useEffect, useRef, useState, useMemo } from 'react';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';

// --- Types & Constants ---
interface CoordinateState {
  theta: number; // Yaw
  phi: number;   // Pitch
  r: number;     // Radius
}

const AXIS_LEN = 12;

const App: React.FC = () => {
  // --- State ---
  const [coords, setCoords] = useState<CoordinateState>({
    theta: 45,
    phi: 30,
    r: 8
  });

  // --- Refs for Three.js objects ---
  const mountRef = useRef<HTMLDivElement>(null);
  const sceneRef = useRef<{
    scene: THREE.Scene;
    camera: THREE.PerspectiveCamera;
    renderer: THREE.WebGLRenderer;
    sphereMesh: THREE.Mesh;
    radiusLine: THREE.Line;
    projLine: THREE.Line;
    heightLine: THREE.Line;
    controls: OrbitControls;
  } | null>(null);

  // --- Math Logic (Extracted for UI & 3D sync) ---
  const mathResult = useMemo(() => {
    const { theta, phi, r } = coords;

    // --- CHANGE HERE: Invert the Phi (Pitch) direction ---
    // Original was: const radPhi = phi * Math.PI / 180;
    const radTheta = theta * Math.PI / 180;
    const radPhi = -phi * Math.PI / 180; // Inverted rotation direction

    const sinT = Math.sin(radTheta);
    const cosT = Math.cos(radTheta);
    const sinP = Math.sin(radPhi);
    const cosP = Math.cos(radPhi);

    // Unity Coordinate Calculation
    const r_xz = r * cosP; // Projection length on XZ plane
    const valX = r_xz * sinT;
    const valY = r * sinP;
    const valZ = r_xz * cosT;

    return {
      radTheta,
      radPhi,
      sinT, cosT, sinP, cosP,
      r_xz,
      valX, valY, valZ
    };
  }, [coords]);

  // --- Initialize Three.js Scene ---
  useEffect(() => {
    if (!mountRef.current) return;

    // 1. Scene Setup
    const scene = new THREE.Scene();
    scene.background = new THREE.Color(0x1a1a1a);

    const width = mountRef.current.clientWidth;
    const height = mountRef.current.clientHeight;
    const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000);
    camera.position.set(0, 5, 20);

    const renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize(width, height);
    renderer.setPixelRatio(window.devicePixelRatio);
    mountRef.current.appendChild(renderer.domElement);

    const controls = new OrbitControls(camera, renderer.domElement);
    controls.enableDamping = true;

    // 2. Helpers
    const gridHelper = new THREE.GridHelper(30, 30, 0x444444, 0x222222);
    scene.add(gridHelper);

    // 3. Axes Creation Helper
    const createLine = (start: THREE.Vector3, end: THREE.Vector3, color: number, dashed = false) => {
      const points = [start, end];
      const geo = new THREE.BufferGeometry().setFromPoints(points);
      let mat: THREE.Material;
      if (dashed) {
        mat = new THREE.LineDashedMaterial({ color, dashSize: 0.5, gapSize: 0.3, scale: 1 });
      } else {
        mat = new THREE.LineBasicMaterial({ color, linewidth: 2 });
      }
      const line = new THREE.Line(geo, mat);
      if (dashed) line.computeLineDistances();
      return line;
    };

    // Axes
    scene.add(createLine(new THREE.Vector3(0, 0, 0), new THREE.Vector3(AXIS_LEN, 0, 0), 0xff0000)); // X
    scene.add(createLine(new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, AXIS_LEN, 0), 0x00ff00)); // Y
    scene.add(createLine(new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 0, -AXIS_LEN), 0x0066ff)); // Z (Visualized into screen)

    // Labels (Using Sprites)
    const addLabel = (text: string, pos: THREE.Vector3, colorStr: string) => {
      const canvas = document.createElement('canvas');
      const ctx = canvas.getContext('2d');
      if (ctx) {
        canvas.width = 64; canvas.height = 64;
        ctx.fillStyle = colorStr;
        ctx.font = "bold 48px Arial";
        ctx.textAlign = "center";
        ctx.textBaseline = "middle";
        ctx.fillText(text, 32, 32);
        const texture = new THREE.CanvasTexture(canvas);
        const spriteMat = new THREE.SpriteMaterial({ map: texture });
        const sprite = new THREE.Sprite(spriteMat);
        sprite.position.copy(pos);
        sprite.scale.set(1.5, 1.5, 1.5);
        scene.add(sprite);
      }
    };

    addLabel("X", new THREE.Vector3(AXIS_LEN + 1, 0, 0), "#ff5555");
    addLabel("Y", new THREE.Vector3(0, AXIS_LEN + 1, 0), "#55ff55");
    addLabel("Z", new THREE.Vector3(0, 0, -AXIS_LEN - 1), "#5588ff");

    // 4. Dynamic Objects
    const sphereMesh = new THREE.Mesh(
      new THREE.SphereGeometry(0.4, 32, 32),
      new THREE.MeshBasicMaterial({ color: 0xffff00 })
    );
    scene.add(sphereMesh);

    const radiusLine = createLine(new THREE.Vector3(), new THREE.Vector3(), 0xffff00, true);
    const projLine = createLine(new THREE.Vector3(), new THREE.Vector3(), 0x00aa00);
    const heightLine = createLine(new THREE.Vector3(), new THREE.Vector3(), 0x00aaff);
    scene.add(radiusLine, projLine, heightLine);

    // Store refs
    sceneRef.current = {
      scene, camera, renderer, sphereMesh, radiusLine, projLine, heightLine, controls
    };

    // Animation Loop
    const animate = () => {
      requestAnimationFrame(animate);
      controls.update();
      renderer.render(scene, camera);
    };
    animate();

    // Cleanup
    return () => {
      if (mountRef.current) {
        mountRef.current.removeChild(renderer.domElement);
      }
      renderer.dispose();
    };
  }, []);

  // --- Update Scene when Coordinates Change ---
  useEffect(() => {
    if (!sceneRef.current) return;

    const { sphereMesh, radiusLine, projLine, heightLine } = sceneRef.current;
    const { valX, valY, valZ } = mathResult;

    // Unity Z+ is "Forward". In Three.js (WebGL), "Forward" into the screen is Z-.
    // To visually match the Unity inspector logic where Z+ goes "into" the scene relative to camera start:
    const renderX = valX;
    const renderY = valY;
    const renderZ = -valZ; 

    sphereMesh.position.set(renderX, renderY, renderZ);

    const origin = new THREE.Vector3(0, 0, 0);
    const target = new THREE.Vector3(renderX, renderY, renderZ);
    const ground = new THREE.Vector3(renderX, 0, renderZ);

    // Update lines
    radiusLine.geometry.setFromPoints([origin, target]);
    radiusLine.computeLineDistances(); // Necessary for dashed lines
    radiusLine.geometry.attributes.position.needsUpdate = true;

    projLine.geometry.setFromPoints([origin, ground]);
    projLine.geometry.attributes.position.needsUpdate = true;

    heightLine.geometry.setFromPoints([ground, target]);
    heightLine.geometry.attributes.position.needsUpdate = true;

  }, [mathResult]);

  // --- Resize Handler ---
  useEffect(() => {
    const handleResize = () => {
      if (!mountRef.current || !sceneRef.current) return;
      const width = window.innerWidth;
      const height = window.innerHeight;
      const { camera, renderer } = sceneRef.current;
      
      camera.aspect = width / height;
      camera.updateProjectionMatrix();
      renderer.setSize(width, height);
    };
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);

  // --- UI Handlers ---
  const handleChange = (key: keyof CoordinateState, value: number) => {
    setCoords(prev => ({ ...prev, [key]: value }));
  };

  return (
    <div className="relative w-full h-screen bg-neutral-900">
      {/* 3D Canvas Container */}
      <div ref={mountRef} className="w-full h-full cursor-move" />

      {/* UI Overlay */}
      <div className="absolute top-4 left-4 w-[400px] max-h-[90vh] overflow-y-auto custom-scrollbar bg-neutral-800/95 backdrop-blur-sm border border-neutral-700 rounded-lg shadow-2xl p-5 flex flex-col gap-4">
        
        <div className="border-b border-neutral-600 pb-3">
          <h3 className="text-xl font-semibold text-sky-400">Unity Coordinates</h3>
          <p className="text-xs text-neutral-400 mt-1">
            System: X-Right, Y-Up, <span className="text-blue-400 font-bold">Z-Forward (Into Screen)</span>
          </p>
          <p className="text-xs text-amber-400 mt-1 font-mono">
            * Pitch direction inverted
          </p>
        </div>

        {/* Controls */}
        <div className="space-y-4">
          <ControlRow 
            label="Theta (Yaw) [-180 ~ 180]"
            value={coords.theta}
            min={-180} max={180} step={1}
            onChange={(v) => handleChange('theta', v)}
          />
          <ControlRow 
            label="Phi (Pitch) [-90 ~ 90]"
            value={coords.phi}
            min={-90} max={90} step={1}
            onChange={(v) => handleChange('phi', v)}
          />
          <ControlRow 
            label="Radius (r) [1 ~ 15]"
            value={coords.r}
            min={1} max={15} step={0.1}
            onChange={(v) => handleChange('r', v)}
          />
        </div>

        {/* Calculations Display */}
        <div className="mt-2">
          <h4 className="text-sm font-semibold text-amber-400 border-l-4 border-amber-400 pl-2 mb-2">Calculation Logic</h4>
          
          <div className="bg-black/40 p-3 rounded border border-neutral-700 font-mono text-xs leading-relaxed text-neutral-300">
            {/* Step 1 */}
            <div className="mb-3 border-b border-dashed border-neutral-700 pb-2">
              <div className="text-green-400 italic mb-1">// 1. Deg to Rad (Pitch Inverted)</div>
              <div>&theta; = {coords.theta}&deg; &rArr; sin: {mathResult.sinT.toFixed(3)}, cos: {mathResult.cosT.toFixed(3)}</div>
              <div>&phi; = <span className='text-red-400'>-({coords.phi})</span>&deg; &rArr; sin: {mathResult.sinP.toFixed(3)}, cos: {mathResult.cosP.toFixed(3)}</div>
            </div>

            {/* Step 2 */}
            <div className="mb-3 border-b border-dashed border-neutral-700 pb-2">
              <div className="text-green-400 italic mb-1">// 2. XZ Plane Projection</div>
              <div>
                <span className="text-sky-300 font-bold">r_xz</span> = r * cos(&phi;)
              </div>
              <div className="pl-4 text-neutral-400">
                = {coords.r} * {mathResult.cosP.toFixed(3)} = <span className="text-sky-200 font-bold">{mathResult.r_xz.toFixed(3)}</span>
              </div>
            </div>

            {/* Step 3 */}
            <div>
              <div className="text-green-400 italic mb-1">// 3. Unity Vectors</div>
              
              {/* Y */}
              <div className="flex justify-between">
                <span><span className="text-green-400 font-bold">Y (Up)</span> = r * sin(&phi;)</span>
              </div>
              <div className="pl-4 text-neutral-400 mb-1">
                 = {coords.r} * {mathResult.sinP.toFixed(3)} = <span className="text-green-300 font-bold">{mathResult.valY.toFixed(2)}</span>
              </div>

              {/* X */}
              <div className="flex justify-between">
                <span><span className="text-red-400 font-bold">X (Right)</span> = r_xz * sin(&theta;)</span>
              </div>
              <div className="pl-4 text-neutral-400 mb-1">
                = {mathResult.r_xz.toFixed(3)} * {mathResult.sinT.toFixed(3)} = <span className="text-red-300 font-bold">{mathResult.valX.toFixed(2)}</span>
              </div>

              {/* Z */}
              <div className="flex justify-between">
                <span><span className="text-blue-400 font-bold">Z (Fwd)</span> = r_xz * cos(&theta;)</span>
              </div>
              <div className="pl-4 text-neutral-400">
                = {mathResult.r_xz.toFixed(3)} * {mathResult.cosT.toFixed(3)} = <span className="text-blue-300 font-bold">{mathResult.valZ.toFixed(2)}</span>
              </div>
            </div>
          </div>
        </div>

        {/* Result Grid */}
        <div className="grid grid-cols-3 gap-2 font-mono text-sm font-bold text-center shadow-inner">
          <div className="bg-red-900/40 border border-red-500/50 text-red-200 p-2 rounded">
            X: {mathResult.valX.toFixed(2)}
          </div>
          <div className="bg-green-900/40 border border-green-500/50 text-green-200 p-2 rounded">
            Y: {mathResult.valY.toFixed(2)}
          </div>
          <div className="bg-blue-900/40 border border-blue-500/50 text-blue-200 p-2 rounded">
            Z: {mathResult.valZ.toFixed(2)}
          </div>
        </div>

      </div>

      {/* Footer Tooltip */}
      <div className="absolute bottom-6 w-full text-center pointer-events-none select-none">
        <span className="text-neutral-500 text-sm drop-shadow-md bg-black/50 px-3 py-1 rounded-full">
          Left Click: Rotate · Right Click: Pan · Scroll: Zoom
        </span>
      </div>
    </div>
  );
};

// --- Sub-component for Slider+Input ---
interface ControlRowProps {
  label: string;
  value: number;
  min: number;
  max: number;
  step: number;
  onChange: (val: number) => void;
}

const ControlRow: React.FC<ControlRowProps> = ({ label, value, min, max, step, onChange }) => {
  return (
    <div className="flex flex-col gap-1">
      <label className="text-xs text-neutral-400">{label}</label>
      <div className="flex items-center gap-3">
        <input
          type="range"
          min={min} max={max} step={step}
          value={value}
          onChange={(e) => onChange(parseFloat(e.target.value))}
          className="flex-grow h-1 bg-neutral-600 rounded-lg appearance-none cursor-pointer accent-sky-500 hover:accent-sky-400 transition-all"
        />
        <input
          type="number"
          min={min} max={max} step={step}
          value={value}
          onChange={(e) => {
            let v = parseFloat(e.target.value);
            if (isNaN(v)) return;
            if (v < min) v = min;
            if (v > max) v = max;
            onChange(v);
          }}
          className="w-16 bg-neutral-950 border border-neutral-600 text-white text-right text-sm p-1 rounded focus:outline-none focus:border-sky-500 font-mono"
        />
      </div>
    </div>
  );
};

export default App;
