import { ServiceError, TacticService } from "../service/MutableTacticService.ts"
import { useNavigate, useParams } from "react-router-dom"
import { useCallback, useEffect, useMemo, useState } from "react"
import {
    useVisualizer,
    VisualizerState,
    VisualizerStateActionKind,
} from "../visualizer/VisualizerState.ts"
import { getParent } from "../domains/StepsDomain.ts"
import { mapToParentContent } from "../domains/TacticContentDomains.ts"
import StepsTree from "../components/editor/StepsTree.tsx"
import { StepInfoNode } from "../model/tactic/Tactic.ts"
import SplitLayout from "../components/SplitLayout.tsx"
import { LocalStorageTacticService } from "../service/LocalStorageTacticService.ts"
import { APITacticService } from "../service/APITacticService.ts"

import "../style/visualizer.css"
import { VisualizerFrame } from "../components/Visualizer.tsx"
import { useAppFetcher } from "../App.tsx"

export interface VisualizerPageProps {
    guestMode: boolean
}

export function VisualizerPage({ guestMode }: VisualizerPageProps) {
    const { tacticId: idStr } = useParams()
    const navigate = useNavigate()

    const fetcher = useAppFetcher()
    if (guestMode || !idStr) {
        return (
            <ServedVisualizerPage
                service={LocalStorageTacticService.init()}
                openEditor={() => navigate("/tactic/edit-guest")}
            />
        )
    }

    return (
        <ServedVisualizerPage
            service={new APITacticService(fetcher, parseInt(idStr))}
            openEditor={() => navigate(`/tactic/${idStr}/edit`)}
        />
    )
}

interface VisualizerService {
    selectStep(step: number): Promise<void | ServiceError>

    openEditor(): Promise<void>
}

interface ServedVisualizerPageProps {
    service: TacticService
    openEditor: () => void
}

function ServedVisualizerPage({
    service,
    openEditor,
}: ServedVisualizerPageProps) {
    const [panicMessage, setPanicMessage] = useState<string>()
    const [state, dispatch] = useVisualizer(null)
    const [canEdit, setCanEdit] = useState(false)

    useEffect(() => {
        async function init() {
            const contextResult = await service.getContext()
            if (typeof contextResult === "string") {
                setPanicMessage(
                    "There has been an error retrieving the editor initial context : " +
                        contextResult,
                )
                return
            }

            const stepId = contextResult.stepsTree.id
            const contentResult = await service.getContent(stepId)

            if (typeof contentResult === "string") {
                setPanicMessage(
                    "There has been an error retrieving the tactic's root step content : " +
                        contentResult,
                )
                return
            }

            setCanEdit(await service.canBeEdited())

            dispatch({
                type: VisualizerStateActionKind.INIT,
                state: {
                    stepId,
                    stepsTree: contextResult.stepsTree,
                    courtType: contextResult.courtType,
                    tacticName: contextResult.name,
                    content: contentResult,
                    parentContent: null,
                },
            })
        }

        if (state === null) init()
    }, [service, state])

    const visualizerService: VisualizerService = useMemo(
        () => ({
            async selectStep(step: number): Promise<void | ServiceError> {
                const result = await service.getContent(step)
                if (typeof result === "string") return result
                const stepParent = getParent(state!.stepsTree!, step)?.id
                let parentContent = null
                if (stepParent) {
                    const parentResult = await service.getContent(stepParent)
                    if (typeof parentResult === "string") return parentResult
                    parentContent = mapToParentContent(parentResult)
                }
                dispatch({
                    type: VisualizerStateActionKind.SET_CONTENTS,
                    content: result,
                    parentContent,
                    stepId: step,
                })
            },
            async openEditor() {
                openEditor()
            },
        }),
        [openEditor, service, state],
    )

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

    if (state === null) {
        return <p>Retrieving tactic context. Please wait...</p>
    }

    return (
        <VisualizerPageContent
            state={state}
            service={visualizerService}
            showEditButton={canEdit}
        />
    )
}

interface VisualizerPageContentProps {
    state: VisualizerState
    service: VisualizerService
    showEditButton: boolean
}

function VisualizerPageContent({
    state: { content, parentContent, stepId, stepsTree, courtType, tacticName },
    service,
    showEditButton,
}: VisualizerPageContentProps) {
    const [isStepsTreeVisible, setStepsTreeVisible] = useState(true)

    const [editorContentCurtainWidth, setEditorContentCurtainWidth] =
        useState(80)

    const stepsTreeNode = (
        <div id={"steps-div"}>
            <StepsTree
                root={stepsTree}
                selectedStepId={stepId}
                onStepSelected={useCallback(
                    (node: StepInfoNode) => service.selectStep(node.id),
                    [service],
                )}
            />
        </div>
    )

    const contentNode = (
        <div id="content-div">
            <VisualizerFrame
                content={content}
                parentContent={parentContent}
                courtType={courtType}
            />
        </div>
    )

    return (
        <div id="visualizer">
            <div id="header-page">
                <p id="title">{tacticName}</p>
                <div id="header-page-right">
                    <button
                        id={"show-steps-button"}
                        onClick={() => setStepsTreeVisible((b) => !b)}>
                        ETAPES
                    </button>
                    {showEditButton && (
                        <button onClick={() => service.openEditor()}>
                            EDITER
                        </button>
                    )}
                </div>
            </div>

            <div id="editor-div">
                {isStepsTreeVisible ? (
                    <SplitLayout
                        rightWidth={editorContentCurtainWidth}
                        onRightWidthChange={setEditorContentCurtainWidth}>
                        {contentNode}
                        {stepsTreeNode}
                    </SplitLayout>
                ) : (
                    contentNode
                )}
            </div>
        </div>
    )
}
