import { Fetcher } from "../app/Fetcher.ts"
import {
    StepInfoNode,
    Tactic,
    TacticInfo,
    TacticStep,
} from "../model/tactic/TacticInfo.ts"
import { APITacticService } from "../service/APITacticService.ts"
import {
    TacticContext,
    TacticService,
} from "../service/MutableTacticService.ts"
import { countSteps } from "./StepsDomain.ts"

export function importTacticFromFile(
    fetcher: Fetcher,
    blob: Blob,
    onSuccess: (tactic: TacticInfo) => void,
    onError: (e: Error | unknown) => void = console.error,
) {
    const reader = new FileReader()
    reader.onloadend = async (e) => {
        const jsonString = e.target!.result as string

        let tactic
        try {
            tactic = await importTactic(fetcher, JSON.parse(jsonString))
        } catch (e) {
            onError(e)
            return
        }
        onSuccess(tactic)
    }
    reader.readAsText(blob)
}

export async function importTactic(
    fetcher: Fetcher,
    tactic: Tactic,
): Promise<TacticInfo> {
    const response = await fetcher.fetchAPI(
        "tactics",
        {
            name: tactic.name,
            courtType: tactic.courtType,
        },
        "POST",
    )

    if (!response.ok) throw Error("Received unsuccessful response from API.")

    const { id, rootStepId } = await response.json()

    const service = new APITacticService(fetcher, id)

    await service.saveContent(rootStepId, tactic.root.content)

    async function importStepChildren(parent: TacticStep, parentId: number) {
        return await Promise.all(
            parent.children.map(async (child) => {
                const result = await service.addStep(parentId, child.content)
                if (typeof result === "string") throw Error(result)
                await importStepChildren(child, result.id)
                return result
            }),
        )
    }

    const rootStepNode: StepInfoNode = {
        id: rootStepId,
        children: await importStepChildren(tactic.root, rootStepId),
    }

    return { courtType: tactic.courtType, name: tactic.name, id, rootStepNode }
}

export async function loadPlainTactic(
    context: TacticContext,
    service: TacticService,
    onProgress: (p: number) => void = () => {},
): Promise<Tactic> {
    const tree = context.stepsTree

    const treeSize = countSteps(tree)
    const totalStepsCompleted = new Uint16Array(1)

    async function transformToStep(
        stepInfoNode: StepInfoNode,
    ): Promise<TacticStep> {
        const contentResult = await service.getContent(stepInfoNode.id)
        if (typeof contentResult === "string") throw Error(contentResult)

        Atomics.add(totalStepsCompleted, 0, 1)
        onProgress((Atomics.load(totalStepsCompleted, 0) / treeSize) * 100)

        return {
            content: contentResult,
            children: await Promise.all(
                stepInfoNode.children.map(transformToStep),
            ),
        }
    }

    return {
        name: context.name,
        courtType: context.courtType,
        root: await transformToStep(tree),
    }
}
