
import SequenceEvaluator from "../domain/SequenceEvaluator";
import SequenceEvaluatorRepository from "../domain/SequenceEvaluatorRepository";
import Step from "../domain/Step";
import StepFactory from "../domain/StepFactory";
import AntlrStepParser from "./AntlrStepParser";
import {sequenceEvaluatorModel} from "./SequenceEvaluatorSchema";

const MAX_EVALUATOR_CACHE = 5;

export default class SequenceEvaluatorRepositoryMongo implements SequenceEvaluatorRepository {

    public evaluatorCache: Map<string, SequenceEvaluator>;
    private stepFactory: StepFactory;

    constructor() {
        this.evaluatorCache = new Map();
        this.stepFactory = new StepFactory(new AntlrStepParser());
    }

    public getSequenceEvaluatorByWebSiteId(webSiteId: string): Promise<SequenceEvaluator | null> {
        if (this.evaluatorCache.has(webSiteId)) {
            return Promise.resolve(this.evaluatorCache.get(webSiteId));
        } else {
            return sequenceEvaluatorModel.findOne({webSiteId})
            .then((sequenceEvaluatorData) => {
                if (sequenceEvaluatorData) {
                    const evaluator = new SequenceEvaluator(sequenceEvaluatorData.name, sequenceEvaluatorData._id);
                    return Promise.all(sequenceEvaluatorData.stepList.map((stepData) => this.stepFactory.createStep(stepData.name, stepData.expression)))
                        .then((values) => {
                            const steplist = values as Step[];
                            evaluator.stepList = steplist;
                            return evaluator;
                        });
                }
            })
            .then((evaluator) => {
                if (!evaluator) {
                    return null;
                }
                this.evaluatorCache.set(webSiteId, evaluator);
                if (this.evaluatorCache.size > MAX_EVALUATOR_CACHE) {
                    this.evaluatorCache.delete(Array.from(this.evaluatorCache.keys())[0]);
                }
                return evaluator;
            });
        }
    }

    public createSequenceEvaluator(name: string, webSiteId: string, stepList: Array<{name: string, expression: string}>): Promise<SequenceEvaluator> {
        let evaluator;
        return sequenceEvaluatorModel.create({
            name,
            webSiteId,
            stepList,
        }).then((createdSequenceEvaluator) => {
            evaluator = new SequenceEvaluator(createdSequenceEvaluator.name, createdSequenceEvaluator._id);
            return Promise.all(createdSequenceEvaluator.stepList.map((stepData) => this.stepFactory.createStep(stepData.name, stepData.expression)));
        }).then(() => {
            return evaluator;
        }).catch((error) => {
            console.log(error);
        });
    }

    public updateSequenceEvaluator(name: string, webSiteId: string, stepList: Array<{name: string, expression: string}>): Promise <void> {
        return sequenceEvaluatorModel.findOneAndUpdate({
            webSiteId,
        }, {
            name,
            stepList,
        });
    }

    public removeSequenceEvaluator(sequenceEvaluator: SequenceEvaluator): Promise<void> {
        return sequenceEvaluatorModel.removeOne({_id: sequenceEvaluator.id});
    }

}
