// @ts-ignore
import {StateBranchNode} from "./StateBranchNode";
import {TestableNode, TestableNodeWithBranchAndFlow} from "./TestableNode";
import {TestableCompositeData} from "./store";
import {Testable} from "./Testable";
import {Testables} from "./testables";
import {TreeNode} from "./TreeNode";
import {
    FirstRenderedNodeData,
    MiddleRenderedNodeData,
    RendererNode,
    RendererNodeData,
    RendererNodeType
} from "./RendererNodeData";
import {FlowDataWithoutPosition, RenderedNodeData} from "./FlowDataCreator";
import {generateIdForType} from "./NodeHelpers";
import {AfterRemovingFromGroveHook} from "./Grove";
import {DataExporter, ExportImportTarget} from "./export/Exporters";
import {GroveExporter} from "./export/GroveExporter";

export class TestableCompositeNode implements TestableNodeWithBranchAndFlow, MiddleRenderedNodeData, RendererNode, FirstRenderedNodeData, AfterRemovingFromGroveHook, RendererNodeData, DataExporter<any> {
    // @ts-ignore
    public branch: StateBranchNode
    // @ts-ignore
    private treeNode: TreeNode;
    constructor(
        private readonly testables: Testables,
        public readonly testableId: string = generateIdForType('testable.group') || '',
    ) {}

    setBranch(branch: StateBranchNode) {
        this.branch = branch;
        // @ts-ignore
        this.branch.setTestable(this);
    }

    setTreeNode(treeNode: TreeNode): void {
        this.treeNode = treeNode;
    }
    getTreeNode(): TreeNode {
        return this.treeNode;
    }
    getId(): string {
        return this.testableId;
    }

    getTestable(): Testable {
        return new Testable(this.testables, this.testableId, true) // <- this 'true' is VERY important!
    }

    isBaseTestableForTier(): boolean {
        return this.branch.isTiered() && this.branch.getBaseTestable()?.id === this.testableId
    }

    getSourceRenderNode(): RendererNodeType | undefined {
        if (this.getTestable().isPartial()) {
            // this is a partial so its source is the step anchor which is represented by the treenode
            return this.treeNode
        } else if (this.isBaseTestableForTier()) {
            // when this is the testable base for a tiered branch, the source is the branch
            return this.branch
        } else if (this.isRoot()) {
            // the source is always its anchor because this is a root tree node!
            return this.treeNode.sourceAnchor as RendererNodeType
        }

        /*
        * if (this.isRoot() && !this.treeNode.isFirstRoot()) {
            // the source is the previous tree node!
            return this.treeNode.grove.getPreviousTreeNode(this.treeNode.getId())
        }
        */
    }

    getSourceRenderNodeId(): string {
        return this.getSourceRenderNode()?.getId() || '';
    }

    getTargetRenderNode(): RendererNodeType | undefined {
        if (this.getTestable().isPartial()) {
            // this is a partial so its target is the branch
            return this.branch
        } else if (this.isBaseTestableForTier()) {
            // let's cheat a little bit, let's get the grove from the tree node to
            // avoid adding another dependency to this class (and the whole modification of source code and tests that this would imply)
            return this.treeNode.grove.get<TreeNode>(this.branch.getFirstChildId())
        }
        // this is when this is a root testable composite
        return this.branch;

    }

    getTargetRenderNodeId() {
        return this.getTargetRenderNode()?.getId() || '';
    }

    getMode(): TestableCompositeData["mode"] {
        return this.getTestable().getMode();
    }

    // this is going to be useful for the path/OR branches, currently not implemented
    isRoot(): boolean {
        return this.getTreeNode().isRoot();
    }

    exportToState() {
        return this.testableId as string;
    }

    exportData(target: ExportImportTarget, groveExporter?: GroveExporter): any {
        /**
         * Ok, so here we need to call the exporterx
         */
        return this.getTestable().export(target)
    }

    createFlowData(): FlowDataWithoutPosition {
        let type: RenderedNodeData['type']

        if (this.treeNode.isTier()) {
            type = 'branch.tiered.tier.testable'
        } else {
            type = 'testable.group'
        }
        return {
            renderedNodes: [
                this.treeNode.createRenderedNodeWithData({
                    id: this.getId(),
                    type,
                })
            ],
            edges: [
                /**
                 * The TreeNode is responsible for connecting the testable to the branch
                 */
            ]
        };
    }

    leftSpacing(): number {
        if (this.isBaseTestableForTier()) {
            return 28
        } else if (this.treeNode.isTier()) {
            return 16
        }

        return 0
    }

    topSpacing(): number {
        if (this.treeNode.isRoot()) {
            return (112 + 20) - 4
        }

        return 50
    }

    afterRemovingFromGrove()  {
        // this is where we remove the testable data from the state
        this.testables.remove(this.testableId)
    };
}