import { useFrame } from "@react-three/fiber";
import { useRef } from "react";
import * as THREE from "three";
import { ARENA_CENTER, ARENA_WALL_RADIUS } from "../../game/arena";
import { BOSS_ARENA_PORTAL_HALF_WIDTH } from "../../game/rpgRoguelike/playSpace";

export interface RpgRoomPortalsProps {
  entryOpen: boolean;
  exitOpen: boolean;
  accent: string;
  wallColor: string;
}

interface PortalDoorwayProps {
  open: boolean;
  accent: string;
  wallColor: string;
  position: readonly [number, number, number];
  rotationY: number;
  hideNearCamera?: boolean;
}

const DOOR_HEIGHT = 3.25;
const DOOR_DEPTH = 0.24;
const FRAME_DEPTH = 0.72;
const FRAME_POST_WIDTH = 0.48;
const OPEN_ANGLE = Math.PI * 0.56;
const OPEN_DAMPING = 8;
const CAMERA_HIDE_RADIUS = 3.6;

function DoorLeaf({ side, accent, wallColor }: {
  side: "left" | "right";
  accent: string;
  wallColor: string;
}) {
  const leafWidth = BOSS_ARENA_PORTAL_HALF_WIDTH;
  const centerX = side === "left" ? leafWidth * 0.5 : -leafWidth * 0.5;
  return (
    <group position={[centerX, DOOR_HEIGHT * 0.5, 0]}>
      <mesh castShadow receiveShadow>
        <boxGeometry args={[leafWidth - 0.035, DOOR_HEIGHT, DOOR_DEPTH]} />
        <meshStandardMaterial
          color={wallColor}
          emissive={accent}
          emissiveIntensity={0.08}
          metalness={0.36}
          roughness={0.62}
        />
      </mesh>
      {[-0.9, 0, 0.9].map((y) => (
        <mesh key={y} position={[0, y, DOOR_DEPTH * 0.56]}>
          <boxGeometry args={[leafWidth - 0.12, 0.09, 0.055]} />
          <meshStandardMaterial color={accent} emissive={accent} emissiveIntensity={0.4} metalness={0.7} roughness={0.3} />
        </mesh>
      ))}
    </group>
  );
}

function PortalDoorway({
  open,
  accent,
  wallColor,
  position,
  rotationY,
  hideNearCamera = false,
}: PortalDoorwayProps) {
  const root = useRef<THREE.Group>(null);
  const leftHinge = useRef<THREE.Group>(null);
  const rightHinge = useRef<THREE.Group>(null);

  useFrame(({ camera }, delta) => {
    if (root.current) {
      const distanceToCamera = Math.hypot(
        camera.position.x - position[0],
        camera.position.z - position[2],
      );
      root.current.visible = !hideNearCamera || distanceToCamera >= CAMERA_HIDE_RADIUS;
    }
    if (!leftHinge.current || !rightHinge.current) return;
    const reducedMotion = typeof document !== "undefined"
      && document.documentElement.classList.contains("force-reduced-motion");
    const leftTarget = open ? OPEN_ANGLE : 0;
    const rightTarget = -leftTarget;
    if (reducedMotion) {
      leftHinge.current.rotation.y = leftTarget;
      rightHinge.current.rotation.y = rightTarget;
      return;
    }
    leftHinge.current.rotation.y = THREE.MathUtils.damp(
      leftHinge.current.rotation.y,
      leftTarget,
      OPEN_DAMPING,
      delta,
    );
    rightHinge.current.rotation.y = THREE.MathUtils.damp(
      rightHinge.current.rotation.y,
      rightTarget,
      OPEN_DAMPING,
      delta,
    );
  });

  const postX = BOSS_ARENA_PORTAL_HALF_WIDTH + FRAME_POST_WIDTH * 0.5;
  return (
    <group ref={root} position={position} rotation={[0, rotationY, 0]}>
      {([-1, 1] as const).map((side) => (
        <mesh key={side} position={[side * postX, DOOR_HEIGHT * 0.55, 0]} castShadow receiveShadow>
          <boxGeometry args={[FRAME_POST_WIDTH, DOOR_HEIGHT * 1.1, FRAME_DEPTH]} />
          <meshStandardMaterial color={wallColor} roughness={0.82} metalness={0.08} />
        </mesh>
      ))}
      <mesh position={[0, DOOR_HEIGHT, 0]} castShadow receiveShadow>
        <torusGeometry args={[BOSS_ARENA_PORTAL_HALF_WIDTH + 0.04, 0.24, 6, 20, Math.PI]} />
        <meshStandardMaterial color={wallColor} roughness={0.78} metalness={0.1} />
      </mesh>
      <mesh position={[0, DOOR_HEIGHT + BOSS_ARENA_PORTAL_HALF_WIDTH + 0.06, 0.03]} rotation={[0, 0, Math.PI / 4]} castShadow>
        <octahedronGeometry args={[0.34, 0]} />
        <meshStandardMaterial color={accent} emissive={accent} emissiveIntensity={0.65} metalness={0.42} roughness={0.28} />
      </mesh>
      <mesh position={[0, 0.055, 0]} receiveShadow>
        <boxGeometry args={[BOSS_ARENA_PORTAL_HALF_WIDTH * 2 + FRAME_POST_WIDTH, 0.11, FRAME_DEPTH]} />
        <meshStandardMaterial color={wallColor} roughness={0.88} />
      </mesh>

      <group ref={leftHinge} position={[-BOSS_ARENA_PORTAL_HALF_WIDTH, 0, 0]}>
        <DoorLeaf side="left" accent={accent} wallColor={wallColor} />
      </group>
      <group ref={rightHinge} position={[BOSS_ARENA_PORTAL_HALF_WIDTH, 0, 0]}>
        <DoorLeaf side="right" accent={accent} wallColor={wallColor} />
      </group>

      <group visible={!open} position={[0, DOOR_HEIGHT * 0.52, DOOR_DEPTH * 0.76]}>
        <mesh castShadow>
          <boxGeometry args={[BOSS_ARENA_PORTAL_HALF_WIDTH * 1.3, 0.16, 0.12]} />
          <meshStandardMaterial color={accent} emissive={accent} emissiveIntensity={0.48} metalness={0.72} roughness={0.24} />
        </mesh>
        <mesh position={[0, 0, 0.1]} rotation={[0, 0, Math.PI / 4]}>
          <octahedronGeometry args={[0.28, 0]} />
          <meshBasicMaterial color={accent} toneMapped={false} />
        </mesh>
      </group>
    </group>
  );
}

/**
 * Projects the linear RPG route through the boss arena: entry is the south
 * portal (+Z), while the next-room exit is the north portal (-Z).
 */
export function RpgRoomPortals({ entryOpen, exitOpen, accent, wallColor }: RpgRoomPortalsProps) {
  return (
    <group>
      <PortalDoorway
        open={exitOpen}
        accent={accent}
        wallColor={wallColor}
        position={[ARENA_CENTER[0], 0, ARENA_CENTER[1] - ARENA_WALL_RADIUS]}
        rotationY={0}
      />
      <PortalDoorway
        open={entryOpen}
        accent={accent}
        wallColor={wallColor}
        position={[ARENA_CENTER[0], 0, ARENA_CENTER[1] + ARENA_WALL_RADIUS]}
        rotationY={Math.PI}
        hideNearCamera
      />
    </group>
  );
}
