import {
    ReactNode,
    useCallback,
    useEffect,
    useMemo,
    useRef,
    useState,
} from "react"
import { APITacticService } from "../service/APITacticService.ts"
import {
    CourtType,
    StepContent,
    StepInfoNode,
    TacticComponent,
} from "../model/tactic/Tactic.ts"
import { getParent } from "../domains/StepsDomain.ts"
import {
    computeRelativePositions,
    getPhantomInfo,
} from "../domains/PlayerDomains.ts"
import { PlayerInfo, PlayerLike } from "../model/tactic/Player.ts"
import { CourtPlayer } from "./editor/CourtPlayer.tsx"
import { BALL_TYPE } from "../model/tactic/CourtObjects.ts"
import { CourtBallPiece } from "./editor/CourtBall.tsx"
import { CourtAction } from "./editor/CourtAction.tsx"
import { BasketCourt, Court } from "./editor/BasketCourt.tsx"
import { TacticService } from "../service/MutableTacticService.ts"
import { useAppFetcher } from "../App.tsx"

export interface VisualizerProps {
    tacticId: number
    stepId?: number
}

export function Visualizer({ tacticId, stepId }: VisualizerProps) {
    const [panicMessage, setPanicMessage] = useState<string | null>(null)
    const [courtType, setCourtType] = useState<CourtType | null>()
    const [stepsTree, setStepsTree] = useState<StepInfoNode | null>()
    const fetcher = useAppFetcher()
    const service = useMemo(
        () => new APITacticService(fetcher, tacticId),
        [tacticId],
    )

    const isNotInit = !stepsTree || !courtType

    useEffect(() => {
        async function init() {
            const contextResult = await service.getContext()
            if (typeof contextResult === "string") {
                setPanicMessage(contextResult)
                return
            }

            const rootStep = contextResult.stepsTree
            setStepsTree(rootStep)
            setCourtType(contextResult.courtType)
        }
        if (isNotInit) init()
    }, [isNotInit, service])

    if (panicMessage) {
        return <p>{panicMessage}</p>
    }
    if (isNotInit) {
        return <p>Loading...</p>
    }

    return (
        <StepVisualizer
            courtType={courtType}
            stepsTree={stepsTree}
            stepId={stepId}
            service={service}
        />
    )
}

export interface StepVisualizerProps {
    stepId?: number
    stepsTree: StepInfoNode
    courtType: CourtType
    service: TacticService
}

export function StepVisualizer({
    stepId,
    stepsTree,
    courtType,
    service,
}: StepVisualizerProps) {
    const [panicMessage, setPanicMessage] = useState<string | null>(null)
    const [content, setContent] = useState<StepContent | null>(null)
    const [parentContent, setParentContent] = useState<StepContent | null>()

    const isNotInit = !content || !parentContent

    useEffect(() => {
        async function init() {
            const contentStepId = stepId ?? stepsTree.id

            const contentResult = await service.getContent(contentStepId)
            if (typeof contentResult === "string") {
                setPanicMessage(contentResult)
                return
            }

            const stepParent = getParent(stepsTree, contentStepId)
            let parentContent = null
            if (stepParent) {
                const parentResult = await service.getContent(contentStepId)
                if (typeof parentResult === "string") {
                    setPanicMessage(parentResult)
                    return
                }
                parentContent = parentResult
            }

            setContent(contentResult)
            setParentContent(parentContent)
        }

        if (isNotInit) init()
    }, [isNotInit, service, stepId, stepsTree])

    if (panicMessage) {
        return <p>{panicMessage}</p>
    }

    if (isNotInit) {
        return <p>Loading Content...</p>
    }

    return (
        <VisualizerFrame
            content={content}
            parentContent={parentContent}
            courtType={courtType}
        />
    )
}

export interface VisualizerFrameProps {
    content: StepContent
    parentContent: StepContent | null
    courtType: CourtType
}

export function VisualizerFrame({
    content,
    parentContent,
    courtType,
}: VisualizerFrameProps) {
    const courtRef = useRef<HTMLDivElement>(null)

    const courtBounds = useCallback(
        () => courtRef.current!.getBoundingClientRect(),
        [courtRef],
    )

    const relativePositions = useMemo(() => {
        const courtBounds = courtRef.current?.getBoundingClientRect()
        return courtBounds
            ? computeRelativePositions(courtBounds, content)
            : new Map()
    }, [content, courtRef])

    const renderPlayer = useCallback(
        (component: PlayerLike, isFromParent: boolean) => {
            let info: PlayerInfo
            const isPhantom = component.type == "phantom"
            const usedContent = isFromParent ? parentContent! : content

            if (isPhantom) {
                info = getPhantomInfo(
                    component,
                    usedContent,
                    relativePositions,
                    courtBounds(),
                )
            } else {
                info = component
            }

            const className =
                (isPhantom ? "phantom" : "player") +
                " " +
                (isFromParent ? "from-parent" : "")

            return (
                <CourtPlayer
                    key={component.id}
                    playerInfo={info}
                    className={className}
                    availableActions={() => []}
                />
            )
        },
        [content, courtBounds, parentContent, relativePositions],
    )

    const renderComponent = useCallback(
        (component: TacticComponent, isFromParent: boolean): ReactNode => {
            if (component.type === "player" || component.type === "phantom") {
                return renderPlayer(component, isFromParent)
            }
            if (component.type === BALL_TYPE) {
                return <CourtBallPiece key="ball" pos={component.pos} />
            }
            return <></>
        },
        [renderPlayer],
    )

    const renderActions = useCallback(
        (component: TacticComponent, isFromParent: boolean) =>
            component.actions.map((action, i) => {
                return (
                    <CourtAction
                        key={"action-" + component.id + "-" + i}
                        action={action}
                        origin={component.id}
                        courtRef={courtRef}
                        color={isFromParent ? "gray" : "black"}
                        isEditable={false}
                    />
                )
            }),
        [courtRef],
    )

    return (
        <BasketCourt
            parentComponents={parentContent?.components ?? null}
            components={content.components}
            courtImage={<Court courtType={courtType} />}
            courtRef={courtRef}
            renderComponent={renderComponent}
            renderActions={renderActions}
        />
    )
}
