import { create } from "domain";
import Action from "../domain/Action";
import ActionInteraction from "../domain/ActionInteraction";
import Comment from "../domain/Comment";
import CommentInteraction from "../domain/CommentInteraction";
import Interaction from "../domain/Interaction";
import Session from "../domain/Session";
import SessionRepository from "../domain/SessionRepository";
import Tester from "../domain/Tester";
import WebSiteRepository from "../domain/WebSiteRepository";
import ExplorationSchema from "./ExplorationSchema";
import SessionSchema from "./SessionSchema";

export default class SessionRepositoryMongo implements SessionRepository {

    public webSiteRepository: WebSiteRepository;
    constructor(webSiteRepository) {
        this.webSiteRepository = webSiteRepository;
    }

    public addSession(session: Session): Promise<string> {
        return SessionSchema.create({
            _id: session.id,
            webSiteId: session.webSite.id,
            baseURL: session.baseURL,
        })
        .then( () => {
            return session.id;
        });
    }

    public addExploration(sessionId: string, explorationNumber: number, tester: Tester): Promise<number> {
        return ExplorationSchema.create({
            sessionId,
            explorationNumber,
            testerName: tester.name,
            isStopped: false,
            interactionList : [],
        }).
        then( () => {
            return explorationNumber;
        });
    }

    public updateInteractionListOfExploration(sessionId: string, explorationNumber: number, updatedInteractionList: Interaction[]): Promise<void> {
        const interactionList = updatedInteractionList.map( (interaction) => {
            if (interaction instanceof ActionInteraction) {
                return {
                    concreteType: "Action",
                    index: interaction.index,
                    kind: interaction.action.kind,
                    value: interaction.action.value,
                };
            }
            if (interaction instanceof CommentInteraction) {
                return {
                    concreteType: "Comment",
                    index: interaction.index,
                    kind: interaction.comment.kind,
                    value: interaction.comment.value,
                };
            }
        });
        return ExplorationSchema.updateOne({sessionId, explorationNumber},
            {
                $set : {
                    interactionList,
                },
            })
            .exec();
    }

    public updateExplorationIsStopped(sessionId: string, explorationNumber: number): Promise<void> {
        return ExplorationSchema.updateOne({sessionId, explorationNumber},
            {
                $set : {
                    isStopped: false,
                },
            })
            .exec();
    }

    public findSessionById(sessionId: string): Promise<Session> {
        let id: string;
        let baseURL: string;
        let session: Session;
        let createdAt: Date;
        let updatedAt: Date;

        return SessionSchema.findOne({_id: sessionId}).lean()
        .then((sessionData) => {
            id = sessionData._id;
            baseURL = sessionData.baseURL;
            createdAt = sessionData.createdAt;
            updatedAt = sessionData.updatedAt;
            return this.webSiteRepository.findWebSiteById(sessionData.webSiteId);
        })
        .then((webSite) => {
            session = new Session(webSite, baseURL, id, createdAt, updatedAt);
        })
        .then(() => {
            return ExplorationSchema.find({sessionId: session.id}).lean().exec();
        })
        .then((explorationDataList) => {
            explorationDataList
            .sort( (explA, explB) => explA.explorationNumber - explB.explorationNumber)
            .forEach((explorationData) => {
                const tester: Tester = new Tester(explorationData.testerName);
                const explorationNumber: number = session.startExploration(tester);
                const interactionList = explorationData.interactionList
                .sort( (interA, interB) => interA.index - interB.index)
                .map((interaction) => {
                    if (interaction.concreteType === "Action") {
                        return new ActionInteraction(interaction.index, new Action(interaction.kind, interaction.value));
                    }
                    if (interaction.concreteType === "Comment") {
                        return new CommentInteraction(interaction.index, new Comment(interaction.kind, interaction.value));
                    }
                });
                session.addInteractionListToExploration(explorationNumber, interactionList);
            });
            return session;
        });
    }

}
