import {NodesStore, TestableCompositeDataWithChildren, TestableData} from "../store";
import {BranchData} from "../StateBranchNode";
import {GroveActions} from "../GroveActions";
import {TreeNodeFactory} from "../TreeNodeFactory";
import {TestableFormat, Testables} from "../testables";
import {PreloadedDynamicValues} from "./ComponentExporterImporter";
import {SelectOption} from "../fields/MultiSelectSearch";
import {fieldsCacheContainer} from "../FieldsCache";
import {atomsStore, ModelAtom} from "../atoms";
import {CouponsPlus} from "../../globals";

export type ExportableDataFormat<Grove = ExportedTree | ExportedTreePreset, Preconditions = TestableCompositeDataWithChildren | Omit<TestableCompositeDataWithChildren, 'id'>> = {
    version: number;
    grove: Grove[],
    preconditions: Preconditions | undefined
    predates: Preconditions | undefined
}

export type ExportableDataFormatPreset = ExportableDataFormat<ExportedTreePreset, Omit<TestableCompositeDataWithChildren, 'id'>>
export type ExportableDataFormatServer = ExportableDataFormat<ExportedTree, TestableCompositeDataWithChildren> & {
    preloadedData: PreloadedDynamicValues[],
    testablePartialRelations: { id: string, data: TestableData, children: string[] }[],
    autoApply: 'yes' | 'no',
    model?: 'tree' | 'rows'
}

export type ImportableDataFormatServer = ExportableDataFormat & Required<Pick<ExportableDataFormatServer, 'model'>> & {
    autoApply: 'yes' | 'no',
    preloadedData: {
        category: string,
        values: SelectOption[]
    }[]
}

export type ExportedTree = {
    id: string,
    testable: TestableCompositeDataWithChildren,
    branch: BranchData,
    extraData?: any
}

/**
 * We need no ids so that new ones are generated upon import
 * If we export with ids, we might have conflicts in both the dashboard and on the backend engine
 * because we need ids to be unique globally throughout the whole site
 */
export type ExportedTreePreset = {
    // no id
    testable: Omit<TestableCompositeDataWithChildren, 'id'>,
    branch: Omit<BranchData, 'id'>,
    extraData?: any
}

export class AppImporter {
    constructor(
        private writeableState: NodesStore,
        private groveActions: GroveActions,
        private testables: Testables,
        private treeNodeFactory: TreeNodeFactory
    ) {
    }

    importFromJSON(json: string): any {
        this.import(JSON.parse(json) as ExportableDataFormat)
    }

    // register confilc reolsutin when existing trees
    // register conflict resolution when existing preconeitons

    private import({grove, preconditions, predates, ...data}: ExportableDataFormat | ImportableDataFormatServer): void {
        //this.importModel('model' in data? data.model : 'tree');
        this.importGrove(grove);
        this.importPreconditions(preconditions);
        this.importPredates(predates);
        this.importPreloadedData((data as ImportableDataFormatServer).preloadedData || []);
        this.importAutoApply((data as ImportableDataFormatServer).autoApply);
    }

    private importGrove(grove: ExportableDataFormat['grove']) {
        const treeNodes = grove.map(
            this.treeNodeFactory.createFromNestedData.bind(this.treeNodeFactory)
        ).filter(v => v !== undefined);

        treeNodes?.forEach?.((treeNode) => {
            this.groveActions.addTreeNode(treeNode)
        })
    }

    private importPreconditions(preconditions: ExportableDataFormat['preconditions']) {
        if (!preconditions || !preconditions.children?.length) {
            return;
        }
        // add the testable composite to the store
        // then add the id to the preconditions id in the store
        // @ts-ignore
        const addedGroupID = this.testables.addTestables([preconditions], TestableFormat.Nested, false)[0]

        if (!addedGroupID) {
            return;
        }

        this.writeableState.preconditionsID = addedGroupID
    }
    private importPredates(predates: ExportableDataFormat['predates']) {
        if (!predates || !predates.children?.length) {
            return;
        }
        // add the testable composite to the store
        // then add the id to the predates id in the store
        // @ts-ignore
        const addedGroupID = this.testables.addTestables([predates], TestableFormat.Nested, false)[0]

        if (!addedGroupID) {
            return;
        }

        this.writeableState.predatesID = addedGroupID
    }

    private importPreloadedData(preloadedData: ImportableDataFormatServer['preloadedData']) {
        /**
         * add the preloaded dynamic data to the cache
         */
        preloadedData.forEach(({category, values}) => {
            const fieldsCache = fieldsCacheContainer.get(category)

            fieldsCache.setValues(values)
        })
    }

    private importAutoApply(autoApply: "yes" | "no") {
        this.writeableState.autoApply = autoApply === 'yes'
    }

    private importModel(model: ExportableDataFormatServer['model']) {
        atomsStore.set(ModelAtom, model || 'tree')
    }
}