const fetch = require('node-fetch');
const requestWebSiteFromToken = require('../tokenUtilities').requestWebSiteFromToken;
const addSession = require('../tokenUtilities').addSession;
const removeSession = require('../tokenUtilities').removeSession;

const DEFAULT_INTERPOLATION_FACTOR = 2;
const DEFAULT_DEPTH = 8;



module.exports = function attachRoutes(app, config) {

    app.get('/dashboard/session/start', (req, res) => {
        let webSiteName = req.query.webSiteName;
        
        requestWebSiteFromToken(req.session.jwt)
            .then(webSiteList => {
                if (webSiteList.length > 0) {
                    res.render('session/start.ejs', {
                        account:req.session, 
                        defaultWebSiteName: webSiteName, 
                        webSiteList: JSON.stringify(webSiteList),
                        webSiteURLList: JSON.stringify(webSiteList.map(site => site.url))
                    });
                } else {
                    let message = 'Cannot start a new session without any WebSite. ';
                    res.render('error.ejs', {message,error:undefined, account:req.session});    
                }
            })
            .catch(error => {
                console.log(error);
                let message = 'Error when fetching WebSite from Security Token';
                res.render('error.ejs', {message,error:e, account:req.session});
            })
    });

    app.post('/dashboard/session/start', (req, res) => {
        let {webSiteId, baseURL, mandatorySteps, interpolationfactor, depth} = req.body;
        
        mandatorySteps = "";
        const mandatoryStepList = mandatorySteps.split("\r\n").filter(step => step.length > 0);
        
        let connectionCode;
        if (interpolationfactor === undefined) {
            interpolationfactor = DEFAULT_INTERPOLATION_FACTOR;
        }
        if (depth === undefined) {
            depth = DEFAULT_DEPTH;
        }

        let sessionId;
        let modelId;

        const sessionCreateURL = 'http://' + config.session.host + ':' + config.session.port + '/session/create';
        let bodySessionCreate = {
            webSiteId,
            baseURL,
        }
        let optionSessionCreate = {
            method: 'POST',
            body:    JSON.stringify(bodySessionCreate),
            headers: { 'Content-Type': 'application/json' },
        }

        const modelCreateURL = 'http://' + config.model.host + ':' + config.model.port + '/model/create';
        let bodyModelCreate = {
            depth,
            interpolationfactor
        }
        let optionModelCreate = {
            method: 'POST',
            body:    JSON.stringify(bodyModelCreate),
            headers: { 'Content-Type': 'application/json' },
        }

        let createSessionPromise = fetch(sessionCreateURL, optionSessionCreate);
        let createModelPromise = fetch(modelCreateURL, optionModelCreate);
        Promise.all([createSessionPromise, createModelPromise])
        .then( responseList => {
            let sessionResponse = responseList[0];
            let modelResponse = responseList[1];
            if (!sessionResponse.ok) {
                throw new Error('session cannot be created', sessionResponse);
            }
            if (!modelResponse.ok) {
                throw new Error('model cannot be created', modelResponse);
            }
            return Promise.all([sessionResponse.json(), modelResponse.json()])
        })
        .then((idList) => {
            sessionId = idList[0];
            modelId = idList[1];
            let linkModel2SessionURL = 'http://' + config.model.host + ':' + config.model.port + '/model/';
            linkModel2SessionURL = linkModel2SessionURL + modelId +'/link/' + sessionId;
            let optionLinkModel = {
                method: 'POST',
                body:    JSON.stringify({}),
                headers: { 'Content-Type': 'application/json' },
            }
            return fetch(linkModel2SessionURL, optionLinkModel);
        })
        .then( response => {
            if (!response.ok) {
                throw new Error('model cannot be linked to session', response);
            }
            connectionCode = `${sessionId}$${modelId}`;
            return addSession(req.session.jwt, connectionCode)
        })
        .then( token => {
            req.session.jwt = token.jwt;
        })
        .then(() => {
            return createEvaluator(sessionId, webSiteId, mandatoryStepList)
        })
        .then(() => {
            res.redirect(`/dashboard/session/view/${connectionCode}`);
        })
        .catch( e => {
            console.log('error', e);
            let message = 'Cannot create the session';
            res.render('error.ejs', {message, account: req.session, error:e});
        })
    })

    app.get('/dashboard/session/view/:connectionCode', (req, res) => {
        const {connectionCode} = req.params;
        const [sessionId, modelId] = connectionCode.split('$');
        const sessionURL = 'http://' + config.session.host + ':' + config.session.port + '/session/'+sessionId;
        const modelURL = 'http://' + config.model.host + ':' + config.model.port + '/model/'+modelId;
        
        let sessionPromise = fetch(sessionURL, {});
        let modelPromise = fetch(modelURL, {});

        let model;
        let session;
        let checklist;
        Promise.all([sessionPromise, modelPromise])
            .then(([responseSession, responseModel]) => {
                if (responseSession.ok && responseModel.ok) {
                    return Promise.all([responseSession.json(), responseModel.json()]);
                } else {
                    let msg = `session:${responseSession.statusText}, model:${responseModel.statusText}`
                    responseSession.json().then(dataError => console.log("Session Error:", dataError ));
                    responseModel.json().then(dataError => console.log("Model Error:", dataError )) ;
                    throw new Error(msg);
                }
            })
            .then(([sessionFetched, modelFetched]) => {
                session = sessionFetched;
                model = modelFetched;
                
                const cross_entropy_past_url = 'http://' + config.model.host + ':' + config.model.port + "/model/cross_entropy_evolution";
                return fetch(cross_entropy_past_url, {
                    method: 'POST',
                    body:    JSON.stringify({
                        sessionId_list: [session.id], 
                        depth: model.depth, 
                        interpolationfactor: model.interpolationfactor
                    }),
                    headers: { 'Content-Type': 'application/json' },
                })
            })
            .then((response) => {
                if (response.ok) {
                    return response.json()
                } else {
                    response.json().then((error) => {
                        throw new Error(error)
                    })
                }
            })
            .then((data) => {
                let crossEntropies = data.map((sequenceData, index) => [index, sequenceData.crossEntropy])
                res.render('session/view.ejs', {
                    account:req.session,
                    session, 
                    model,
                    connectionCode, 
                    socketPort: config.model.socketPort,
                    socketProtocol: config.model.socketProtocol,
                    crossEntropies: JSON.stringify(crossEntropies)});
            })
            .catch(e => {
                console.log(e);
                let message = 'Cannot view the session';
                res.render('error.ejs', {message, error:e, account: req.session})
            });
    });


    app.get('/dashboard/session/:connectionCode/exploration/', (req, res) => {
        const { connectionCode } = req.params;
        const [sessionId, modelId] = connectionCode.split('$');
        const sessionURL = 'http://' + config.session.host + ':' + config.session.port + '/session/'+sessionId;
        const screenshotURL = 'http://' + config.session.host + ':' + config.session.port + '/session/'+sessionId + '/screenshotlist';
        const videoURL = 'http://' + config.session.host + ':' + config.session.port + '/session/'+sessionId + '/videolist';
        // console.log(screenshotURL);
        const sessionPromise = fetch(sessionURL);
        const screenshotPromise = fetch(screenshotURL);
        const videoPromise = fetch(videoURL);
        Promise.all([sessionPromise,screenshotPromise, videoPromise])
            .then(([responseSession, responseScreenshot, responseVideo]) => {
                if (responseSession.ok && responseScreenshot.ok && responseVideo.ok) {
                    return Promise.all([responseSession.json(), responseScreenshot.json(), responseVideo.json()]);
                } else {
                    let msg = `session:${responseSession.statusText}`;
                    throw new Error(msg);
                }
            })
            .then(([session, screenshot, video]) => {
                console.log('video:',video);
                res.render('session/exploration.ejs',{account:req.session, session, screenshot, video});
            })
            .catch(e => {
                let message = 'Cannot fetch the explorations';
                res.render('error.ejs', {message, account:req.session, error:e})
            });
    });

    app.get('/dashboard/session/:connectionCode/json/', (req, res) => {
        const { connectionCode } = req.params;
        const [sessionId, modelId] = connectionCode.split('$');
        const sessionURL = 'http://' + config.session.host + ':' + config.session.port + '/session/'+sessionId;
        fetch(sessionURL, {})
            .then(responseSession => {
                if (responseSession.ok) {
                    return responseSession.json();
                } else {
                    let msg = `session:${responseSession.statusText}`;
                    throw new Error(msg);
                }
            })
            .then(session => {
                res.json(session);
            })
            .catch(e => {
                res.status(500).json({error:e});
            });
    });


    app.get('/dashboard/session/:connectionCode/ngram/', (req, res) => {
        const { connectionCode } = req.params
        const [sessionId, modelId] = connectionCode.split('$');
        const modelURL = 'http://' + config.model.host + ':' + config.model.port + '/model/'+ modelId+'/analyze/allngram';

        fetch(modelURL)
            .then(response => {
                if (response.ok) {
                    return response.json();
                }
            })
            .then(ngrams => {
                let ngramsData = {}
                ngrams.forEach(ngram => {
                    if (!ngramsData[ngram.n]) {
                        ngramsData[ngram.n] = [ngram]
                    } else {
                        ngramsData[ngram.n].push(ngram)
                    }
                });
                for (const ngramArray of Object.values(ngramsData)) {
                    ngramArray.sort((a, b) => b.occurence - a.occurence)
                }
                res.render('session/ngram.ejs', {ngramsData, modelId, account:req.session});
            })
            .catch(e => {
                console.log(e);
                let message = 'Cannot fetch the ngram';
                res.render('error.ejs', {message, account:req.session, error:e});
            })
    });

    app.get("/dashboard/session/:connectionCode/print", (req, res) => {
        const { connectionCode } = req.params
        const [sessionId] = connectionCode.split('$');
        fetch('http://' + config.printer.host + ':' + config.printer.port + '/printer/print/puppeteer/', {
            method: 'POST',
            body: JSON.stringify({
                sessionId
            }),
            headers: { 'Content-Type': 'application/json' },
        })
            .then(response => {
                if (response.ok) {
                    return response.json();
                }
            })
            .then(data => {
                res.set({"Content-Disposition":`attachment; filename="${sessionId}.js"`});
                res.send(data.print)
            })
            .catch(e => {
                console.log(e);
                res.render('error.ejs', {message: e.message, account:req.session, error:e});
            })
    });

    app.post("/dashboard/session/profile_coverage_view/:connectionCode", (req, res) => {
        const { connectionCode } = req.params;
        const { connectionCodeProfile } = req.body;

        res.redirect(`/dashboard/session/profile_coverage/${connectionCode}/${connectionCodeProfile}`);
    })

    app.get("/dashboard/session/profile_coverage/:connectionCode/:connectionCodeProfile", (req, res) => {
        const { connectionCode, connectionCodeProfile } = req.params;

        const modelId = connectionCode.split('$')[1]
        const modelIdProfile = connectionCodeProfile.split('$')[1]

        const modelURL = `http://${config.model.host}:${config.model.port}/model/profile_coverage/${modelId}/${modelIdProfile}`
        fetch(modelURL).then(response => {
            if (response.ok) {
                return response.json()
            }
        }).then((data) => {
            res.render('session/coverage.ejs', {
                account:req.session,
                coverage: data.coverage, 
                connectionCode, 
                connectionCodeProfile
            })
        }).catch(e => {
            console.error(e)
            let message = 'Failed to compute coverage';
            res.render('error.ejs', {message, account:req.session, error:e});
        })
    });

    app.get('/dashboard/session/remove/:connectionCode', (req, res) => {
        const {connectionCode} = req.params;
        removeSession(req.session.jwt , connectionCode)
            .then(token => {
                req.session.jwt = token.jwt;
                res.redirect('/account/account');
            })
    });

    function createEvaluator(sessionId, webSiteId, mandatoryStepList) {
         let objectives = mandatoryStepList.map(text => {
            return ({
                type: "step",
                webSiteId,
                name: "Mandatory Steps",
                text
            });
        });

        if (objectives.length === 0) {
            return Promise.resolve();
        } else {
            const URL = 'http://' + config.objective.host + ':' + config.evaluator.port + '/evaluator/createCheckList'
            return fetch(URL, {
                method: 'POST',
                body: JSON.stringify({sessionId, objectives}),
                headers: { 'Content-Type': 'application/json' },
            })
        }
    }
}
