
import SequenceEvaluator from "../domain/SequenceEvaluator";
import SequenceEvaluatorRepository from "../domain/SequenceEvaluatorRepository";
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 sessionCheckListCache: Map<string, SequenceEvaluator>;
    private stepFactory: StepFactory;

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

    public getSequenceEvaluatorBySessionId(sessionId: string): Promise<SequenceEvaluator | null> {
        if (this.sessionCheckListCache.has(sessionId)) {
            return Promise.resolve(this.sessionCheckListCache.get(sessionId));
        } else {
            return sequenceEvaluatorModel.findOne({sessionId})
            .then((sequenceEvaluatorData) => {
                if (sequenceEvaluatorData) {
                    return this.buildSequenceEvaluator(sequenceEvaluatorData);
                }
            })
            .then((evaluator) => {
                if (!evaluator) {
                    return null;
                }
                this.sessionCheckListCache.set(sessionId, evaluator);
                if (this.sessionCheckListCache.size > MAX_EVALUATOR_CACHE) {
                    this.sessionCheckListCache.delete(Array.from(this.sessionCheckListCache.keys())[0]);
                }
                return evaluator;
            });
        }
    }

    public createSequenceEvaluator(name: string, sessionId: string, stepList: string[]): Promise<SequenceEvaluator> {
        let evaluator;
        return sequenceEvaluatorModel.create({
            name,
            sessionId,
            stepList,
        }).then((createdSequenceEvaluator) => {
            evaluator = new SequenceEvaluator(createdSequenceEvaluator.name, createdSequenceEvaluator._id);
            return Promise.all(createdSequenceEvaluator.stepList.map((expression) => this.stepFactory.createStep(expression)));
        }).then(() => {
            return evaluator;
        });
    }

    public updateSequenceEvaluator(name: string, sessionId: string, stepList: string[]): Promise <void> {
        return sequenceEvaluatorModel.findOneAndUpdate({
            sessionId,
        }, {
            name,
            stepList,
        });
    }

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

    private buildSequenceEvaluator(sequenceEvaluatorData): Promise<SequenceEvaluator> {
        const evaluator = new SequenceEvaluator(sequenceEvaluatorData.name, sequenceEvaluatorData._id);
        return Promise.all(sequenceEvaluatorData.stepList.map((expression) => this.stepFactory.createStep(expression)))
        .then(() => {
            return evaluator;
        });
    }

}
