import { StepInfoNode } from "../model/tactic/Tactic.ts"

export function addStepNode(
    root: StepInfoNode,
    parent: StepInfoNode,
    child: StepInfoNode,
): StepInfoNode {
    if (root.id === parent.id) {
        return {
            ...root,
            children: root.children.concat(child),
        }
    }

    return {
        ...root,
        children: root.children.map((c) => addStepNode(c, parent, child)),
    }
}

export function getStepName(root: StepInfoNode, step: number): string {
    let ord = 1
    const nodes = [root]
    while (nodes.length > 0) {
        const node = nodes.pop()!

        if (node.id === step) break

        ord++
        nodes.push(...[...node.children].reverse())
    }

    return ord.toString()
}

export function getStepNode(
    root: StepInfoNode,
    stepId: number,
): StepInfoNode | undefined {
    if (root.id === stepId) return root

    for (const child of root.children) {
        const result = getStepNode(child, stepId)
        if (result) return result
    }
}

export function removeStepNode(
    root: StepInfoNode,
    targetId: number,
): StepInfoNode | undefined {
    const path = getPathTo(root, targetId)

    path.reverse()

    const [removedNode, ...pathToRoot] = path

    let child = removedNode

    for (const node of pathToRoot) {
        child = {
            id: node.id,
            children: node.children.flatMap((c) => {
                if (c.id === removedNode.id) return []
                else if (c.id === child.id) {
                    return [child]
                }
                return [c]
            }),
        }
    }

    return child
}

export function getPathTo(
    root: StepInfoNode,
    targetId: number,
): StepInfoNode[] {
    if (root.id === targetId) return [root]

    for (const child of root.children) {
        const subPath = getPathTo(child, targetId)
        if (subPath.length > 0) return [root, ...subPath]
    }
    return []
}

/**
 * Returns an available identifier that is not already present into the given node tree
 * @param root
 */
export function getAvailableId(root: StepInfoNode): number {
    const acc = (root: StepInfoNode): number =>
        Math.max(root.id, ...root.children.map(acc))
    return acc(root) + 1
}

export function getParent(
    root: StepInfoNode,
    node: number,
): StepInfoNode | null {
    if (root.children.find((n) => n.id === node)) return root

    for (const child of root.children) {
        const result = getParent(child, node)
        if (result != null) {
            return result
        }
    }
    return null
}
