import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { ReactComponent as TickIcon } from "../assets/green-tick.svg";
import styles from "./styles.module.css";
import { IRefPhaserGame, PhaserGame } from "../tutorial-game/PhaserGame";
import { EventBus } from "../tutorial-game/EventBus";
import { PosterItem } from "../types";
import PlayScene from "../tutorial-game/scenes/PlayScene";

import { ReactComponent as OnTiltLeft } from "../assets/blocks/ontiltleft.svg";
import { ReactComponent as OnTiltRight } from "../assets/blocks/ontiltright.svg";
import { ReactComponent as ForwardIcon } from "../assets/blocks/forward.svg";
import { ReactComponent as BackwardIcon } from "../assets/blocks/backward.svg";

import BlockSelector4 from "../block-selector-4";
import {
    selectedBlockFromTutorialForLeft,
    selectedBlockFromTutorialForRight,
    setSelectedBlockFromTutorialForLeft,
    setSelectedBlockFromTutorialForRight
} from "../utils/selected-blocks-from-tutorial";
import { CommandPair, FallbackTiltButton, SuccessBadge, TutorialFrame } from "./TutorialFrame";

const leftPosterItems: PosterItem[] = [
    { icon: BackwardIcon, block: "moveleft", execute: async () => true },
    { icon: ForwardIcon, block: "moveright", execute: async () => true },
];

const rightPosterItems: PosterItem[] = [
    { icon: ForwardIcon, block: "moveright", execute: async () => true },
    { icon: BackwardIcon, block: "moveleft", execute: async () => true },
];

const staticBlockItemLeft: PosterItem = { icon: OnTiltLeft, block: "ontiltleft", execute: async () => true };
const staticBlockItemRight: PosterItem = { icon: OnTiltRight, block: "ontiltright", execute: async () => true };
const defaultLeftBlock = leftPosterItems[0];
const defaultRightBlock = rightPosterItems[0];
const chooseSteeringGameFrameStyle: React.CSSProperties = {
    width: "100%",
    height: "min(52vh, 560px)",
    visibility: "hidden",
};

type Props = {
    nextPhase: () => void;
};

export function Phase3({ nextPhase }: Props) {
    const [gameContainerDimensions, setGameContainerDimensions] = useState<React.CSSProperties>(chooseSteeringGameFrameStyle);
    const [isSceneReady, setIsSceneReady] = useState(false);
    const [blocksIconState, setBlocksIconState] = useState<"visible" | "animation">("visible");
    const [isComplete, setIsComplete] = useState(false);
    const [selectedLeftBlock, setSelectedLeftBlock] = useState<PosterItem>(() => resolveSteeringBlock(selectedBlockFromTutorialForLeft, defaultLeftBlock, leftPosterItems));
    const [selectedRightBlock, setSelectedRightBlock] = useState<PosterItem>(() => resolveSteeringBlock(selectedBlockFromTutorialForRight, defaultRightBlock, rightPosterItems));
    const [lastPracticeMove, setLastPracticeMove] = useState("");
    const [practiceMoveCount, setPracticeMoveCount] = useState(0);
    const phaserGameRef = useRef<IRefPhaserGame>(null);
    const completionStarted = useRef(false);
    const sceneSetupTimeoutRef = useRef<number | undefined>();
    const nextPhaseTimeoutRef = useRef<number | undefined>();

    const leftInitialIndex = useMemo(() => getPosterItemIndex(selectedLeftBlock, leftPosterItems), [selectedLeftBlock]);
    const rightInitialIndex = useMemo(() => getPosterItemIndex(selectedRightBlock, rightPosterItems), [selectedRightBlock]);
    const controlsReady = true;

    const applySelectedBlocksToScene = useCallback(() => {
        const scene = phaserGameRef.current?.scene;
        if (!scene) return;
        scene.selectedCogTiltLeftBlock = selectedLeftBlock;
        scene.selectedCogTiltRightBlock = selectedRightBlock;
    }, [selectedLeftBlock, selectedRightBlock]);

    const runPracticeTilt = useCallback((direction: "left" | "right") => {
        const scene = phaserGameRef.current?.scene;
        if (!scene?.player || !scene.isGameRunning) return;
        applySelectedBlocksToScene();
        scene.player.actionDecider(direction, "fallback-keyboard");
    }, [applySelectedBlocksToScene]);

    const completeStep = useCallback(() => {
        if (!controlsReady || completionStarted.current) return;
        completionStarted.current = true;
        setSelectedBlockFromTutorialForLeft(selectedLeftBlock);
        setSelectedBlockFromTutorialForRight(selectedRightBlock);
        setBlocksIconState("animation");
        setIsComplete(true);
        nextPhaseTimeoutRef.current = window.setTimeout(nextPhase, 900);
    }, [controlsReady, nextPhase, selectedLeftBlock, selectedRightBlock]);

    useEffect(() => {
        const handleMountainReady = () => {
            setGameContainerDimensions({
                ...chooseSteeringGameFrameStyle,
                visibility: "visible",
            });
        };

        const handleCurrentSceneReady = (scene: PlayScene) => {
            sceneSetupTimeoutRef.current = window.setTimeout(() => {
                if (!scene.sys || !scene.physics?.world || !scene.scale) return;
                scene.physics.world.setBounds(0, 0, scene.scale.width, scene.scale.height);
                scene.player?.setCollideWorldBounds(true);
            }, 1000);
            setIsSceneReady(true);
        };

        const handleActionExecuted = ({ block }: { block?: string } = {}) => {
            if (block !== "moveleft" && block !== "moveright") return;
            setLastPracticeMove(block === "moveleft" ? "left" : "right");
            setPracticeMoveCount(count => count + 1);
        };

        EventBus.on('mountain-ready', handleMountainReady);
        EventBus.on('current-scene-ready', handleCurrentSceneReady);
        EventBus.on('action-executed', handleActionExecuted);

        return () => {
            EventBus.removeListener('action-executed', handleActionExecuted);
            EventBus.removeListener('mountain-ready', handleMountainReady);
            EventBus.removeListener('current-scene-ready', handleCurrentSceneReady);
            if (sceneSetupTimeoutRef.current) {
                window.clearTimeout(sceneSetupTimeoutRef.current);
            }
            if (nextPhaseTimeoutRef.current) {
                window.clearTimeout(nextPhaseTimeoutRef.current);
            }
        }
    }, []);

    useEffect(() => {
        if (!isSceneReady) return;
        applySelectedBlocksToScene();
    }, [applySelectedBlocksToScene, isSceneReady]);

    useEffect(() => {
        const handleKeyDown = (event: KeyboardEvent) => {
            if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
            event.preventDefault();
            event.stopPropagation();
            event.stopImmediatePropagation?.();
            runPracticeTilt(event.key === "ArrowLeft" ? "left" : "right");
        };

        const handleKeyUp = (event: KeyboardEvent) => {
            if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
            event.preventDefault();
            event.stopPropagation();
            event.stopImmediatePropagation?.();
            phaserGameRef.current?.scene?.player?.actionDecider("none", "fallback-keyboard");
        };

        window.addEventListener("keydown", handleKeyDown, true);
        window.addEventListener("keyup", handleKeyUp, true);

        return () => {
            window.removeEventListener("keydown", handleKeyDown, true);
            window.removeEventListener("keyup", handleKeyUp, true);
        };
    }, [runPracticeTilt]);

    const blocksIconContainerClasses = [
        styles.doubleColumnContainer,
        styles.chooseSteeringContainer,
        blocksIconState === "animation" ? styles.fadeOutAnimation : "",
    ].join(" ");

    return (
        <TutorialFrame
            stepIndex={2}
            title="Choose Steering"
            hint="Pick an action for each tilt direction. Choose real movement for both sides."
            controls={
                <>
                    <CommandPair TriggerIcon={OnTiltLeft} ActionIcon={selectedLeftBlock.icon} />
                    <CommandPair TriggerIcon={OnTiltRight} ActionIcon={selectedRightBlock.icon} />
                    <FallbackTiltButton onPress={completeStep} disabled={!controlsReady || isComplete} label="Ready" />
                </>
            }
            badge={isComplete ? <SuccessBadge TickIcon={TickIcon} /> : null}
        >
            <div className={blocksIconContainerClasses} data-testid="choose-steering-practice">
                <div className={`${styles.doubleBlockSelectorContainer} ${styles.selectorFocusAnchor}`}>
                    {isSceneReady && (
                        <>
                            <BlockSelector4
                                gameRef={phaserGameRef}
                                staticBlock="left"
                                posterItems={leftPosterItems}
                                staticBlockItem={staticBlockItemLeft}
                                onSelectedBlockChange={setSelectedLeftBlock}
                                initialSelectedIndex={leftInitialIndex}
                            />
                            <BlockSelector4
                                gameRef={phaserGameRef}
                                staticBlock="right"
                                posterItems={rightPosterItems}
                                staticBlockItem={staticBlockItemRight}
                                onSelectedBlockChange={setSelectedRightBlock}
                                initialSelectedIndex={rightInitialIndex}
                            />
                        </>
                    )}
                </div>
                <div
                    className={`${styles.gameContainer} ${styles.chooseSteeringGame}`}
                    style={gameContainerDimensions}
                    data-testid="choose-steering-game"
                    data-selected-left-block={selectedLeftBlock.block}
                    data-selected-right-block={selectedRightBlock.block}
                    data-last-practice-move={lastPracticeMove}
                    data-practice-move-count={practiceMoveCount}
                >
                    <PhaserGame ref={phaserGameRef} />
                </div>
            </div>
        </TutorialFrame>
    );
}

function getPosterItemIndex(item: PosterItem, posterItems: PosterItem[]) {
    const index = posterItems.findIndex(posterItem => posterItem.block === item.block);
    return index === -1 ? 0 : index;
}

function resolveSteeringBlock(item: PosterItem | undefined, fallback: PosterItem, posterItems: PosterItem[]) {
    return posterItems.find(posterItem => posterItem.block === item?.block) || fallback;
}
