/* jshint esversion: 6 */

const config = require("../../config.js"),
    http = require("http"),
    https = require("https"),
    sessionManager = require("../session.js").SessionManager,
    accountConfigManager = require("../models/accountConfig.js").accountConfigManager;

function recursiveGet(url, cb, redirectLoop) {
    var getFnc = http.get;

    if (url.substr(0, 8) === "https://")
        getFnc = https.get;
    getFnc(url, (d) => {
        if (d.statusCode >= 300 && d.statusCode < 400 && d.headers["location"]) {
            if (!redirectLoop)
                redirectLoop = [];

            if (redirectLoop.indexOf(d.headers["location"]) === -1 && redirectLoop.length < 5) {
                redirectLoop.push(d.headers["location"]);
                recursiveGet(d.headers["location"], cb, redirectLoop);
                return;
            }
        }
        cb(d);
    });
};

module.exports.ApiController = {
    onRequest: function(req, res, srv) {
        if (req.urlObj.match(["api", "logout"])) {
            if (req.method === 'POST') {
                sessionManager.closeSession(req);
            } else {
                res.writeHeader("400", "Bad request");
                sessionManager.saveSession(req.session);
                res.end();
            }
        } else if (req.urlObj.match(["api", "settings"])) {
            if (req.method === 'POST' && req.urlObj.queryTokens.service && req.urlObj.queryTokens.device) {
                let body = null,
                    service  = req.urlObj.queryTokens.service[0],
                    device = req.urlObj.queryTokens.device[0];

                if (service === "null")
                    service = null;
                if (device === "null")
                    device = null;
                req.on('data', (chunk) => {
                    body = body ? Buffer.concat([body, chunk], body.length +chunk.length) : Buffer.from(chunk);
                });
                req.once('end', () => {
                    try {
                        body = JSON.parse(body.toString("utf8"));
                    } catch (e) {
                        body = null;
                    }
                    if (!body) {
                        res.writeHeader("400", "Bad Request");
                        res.end();
                    } else {
                        accountConfigManager.addToConfig(req.account.id, service, device, body, req.reqT, () => {
                            res.writeHeader("204", "No Content");
                            res.end();
                        });
                    }
                    sessionManager.saveSession(req.session);
                });
            } else {
                res.writeHeader("400", "Bad Request");
                res.end();
            }
        } else if (req.urlObj.match(["api", "hist"])) {
            if (!req.urlObj.queryTokens.room) {
                res.writeHeader("400", "Bad request");
                sessionManager.saveSession(req.session);
                res.end();
            } else {
                let ctx = res.chatContext.getChannelContext(req.urlObj.queryTokens.room[0]);
                if (ctx) {
                    ctx.fetchHistory(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]], (hist) => {
                        var histArray = [];
                        hist.forEach((i) => {
                            histArray.push(i.toStatic());
                        });
                        res.writeHeader(200, {"Content-Type":"application/json"});
                        res.end(JSON.stringify(histArray));
                    });
                } else {
                    res.writeHeader("404", "Channel not found");
                    res.end();
                }
            }
        } else if (req.urlObj.match(["api", "typing"])) {
            if (!req.urlObj.queryTokens.room) {
                res.writeHeader("400", "Bad request");
                res.end();
            } else {
                let ctx = res.chatContext.getChannelContext(req.urlObj.queryTokens.room[0]);

                if (!ctx) {
                    res.writeHeader("404", "Chan not found");
                } else {
                    ctx.sendTyping(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]]);
                    res.writeHeader("204", "No Content");
                }
                res.end();
            }
        } else if (req.urlObj.match(["api", "cmd"])) {
            if (!req.urlObj.queryTokens.room ||
                !req.urlObj.queryTokens.cmd) {
                res.writeHeader("400", "Bad request");
                res.end();
            } else {
                let ctx = res.chatContext.getChannelContext(req.urlObj.queryTokens.room[0]),
                    cmd = ctx ? ctx.getChatContext().commands.data['/' +req.urlObj.queryTokens.cmd[0]] : null;

                if (!ctx) {
                    res.writeHeader("404", "Chan not found");
                } else if (!cmd) {
                    res.writeHeader("404", "No such command");
                } else {
                    let args = req.urlObj.queryTokens.args ? req.urlObj.queryTokens.args[0] : "";
                    if (args === true)
                        args = "";
                    ctx.sendCommand(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]], cmd, args);
                    res.writeHeader("204", "No Content");
                }
                res.end();
            }
        } else if (req.urlObj.match(["api", "markread"])) {
            if (!req.urlObj.queryTokens.room || !req.urlObj.queryTokens.id || !req.urlObj.queryTokens.ts) {
                res.writeHeader("400", "Bad request");
                res.end();
            } else {
                let ctx = res.chatContext.getChannelContext(req.urlObj.queryTokens.room[0]),
                    id = req.urlObj.queryTokens.id[0],
                    ts = req.urlObj.queryTokens.ts[0];

                if (!ctx) {
                    res.writeHeader("404", "Chan Not Found");
                } else {
                    ctx.markRead(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]], id, ts);
                    res.writeHeader("204", "No Content");
                }
                res.end();
            }
            sessionManager.saveSession(req.session);
        } else if (req.urlObj.match(["api", "avatar"])) {
            if (!req.urlObj.queryTokens.user) {
                res.writeHeader("400", "Bad request");
                res.end();
            } else {
                let user = res.chatContext.getUser(req.urlObj.queryTokens.user[0]);
                if (!user) {
                    res.writeHeader("404", "User Not Found");
                    res.end();
                } else {
                    let url = (req.urlObj.queryTokens.size && req.urlObj.queryTokens.size[0] === 'l') ? user.getLargeIcon() : user.getSmallIcon();
                    if (!config.isDebug)
                        res.setHeader('Cache-Control', 'private, max-age=' +60 * 60); // 1 hour cache for avatars
                    recursiveGet(url, (d) => {
                        d.pipe(res, { end: true });
                    });
                }
            }
            sessionManager.saveSession(req.session);
        } else if (req.urlObj.match(["api", "msg"])) {
            if (req.method === 'POST') {
                if (!req.urlObj.queryTokens.room || !req.urlObj.queryTokens.text) {
                    res.writeHeader("400", "Bad request");
                } else {
                    let ctx = res.chatContext.getChannelContext(req.urlObj.queryTokens.room[0]);

                    if (ctx) {
                        let attachments = null,
                            pendingId;
                        if (req.urlObj.queryTokens.attachments) {
                            try { attachments = JSON.parse(req.urlObj.queryTokens.attachments[0]); }
                            catch (e) {}
                        }
                        if (req.urlObj.queryTokens.me)
                            pendingId = ctx.sendMeMsg(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]], req.urlObj.queryTokens.text);
                        else
                            pendingId = ctx.sendMsg(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]], req.urlObj.queryTokens.text, attachments);
                        if (pendingId)
                            res.end("{\"pendingId\":\"" +pendingId +"\"}");
                        else
                            res.writeHeader("204", "No Content");
                    } else {
                        res.writeHeader("404", "Channel not found");
                    }
                }
            } else if (req.method === "DELETE") {
                if (!req.urlObj.queryTokens.room || !req.urlObj.queryTokens.ts) {
                    res.writeHeader("400", "Bad request");
                } else {
                    let ctx = res.chatContext.getChannelContext(req.urlObj.queryTokens.room[0]);
                    if (ctx) {
                        ctx.removeMsg(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]], req.urlObj.queryTokens.ts[0]);
                        res.writeHeader("204", "No Content");
                    } else {
                        res.writeHeader("404", "Channel not found");
                    }
                }
            } else if (req.method === "PUT") {
                if (!req.urlObj.queryTokens.room || !req.urlObj.queryTokens.ts || !req.urlObj.queryTokens.text) {
                    res.writeHeader("400", "Bad request");
                } else {
                    let ctx = res.chatContext.getChannelContext(req.urlObj.queryTokens.room[0]);
                    if (ctx) {
                        ctx.editMsg(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]], req.urlObj.queryTokens.ts[0], req.urlObj.queryTokens.text);
                        res.writeHeader("204", "No Content");
                    } else {
                        res.writeHeader("404", "Channel not found");
                    }
                }
            } else {
                res.writeHeader("400", "Bad request");
            }
            sessionManager.saveSession(req.session);
            res.end();
        } else if (req.urlObj.match(["api", "reaction"])) {
            const chanId = req.urlObj.queryTokens.room ? req.urlObj.queryTokens.room[0] : undefined,
                msgId = req.urlObj.queryTokens.msg ? req.urlObj.queryTokens.msg[0] : undefined,
                reaction = req.urlObj.queryTokens.reaction ? req.urlObj.queryTokens.reaction[0] : undefined;

            if (chanId && msgId && reaction) {
                let ctx = res.chatContext.getChannelContext(chanId);
                if (!ctx) {
                    res.writeHeader("404", "Channel Not Found");
                } else if (req.method === 'POST') {
                    res.writeHeader("204", "No Content");
                    ctx.addReaction(ctx.getChatContext().channels[chanId], msgId, reaction);
                } else if (req.method === 'DELETE') {
                    res.writeHeader("204", "No Content");
                    ctx.removeReaction(ctx.getChatContext().channels[chanId], msgId, reaction);
                } else {
                    res.writeHeader("405", "Method not allowed");
                }
            } else {
                res.writeHeader("400", "Missing Parameter");
            }
            sessionManager.saveSession(req.session);
            res.end();
        } else if (req.urlObj.match(["api", "file"])) {
            sessionManager.saveSession(req.session);
            if (req.urlObj.queryTokens.room) {
                let ctx = res.chatContext.getChannelContext(req.urlObj.queryTokens.room[0]);
                if (ctx) {
                    let uploadRequest = ctx.openUploadFileStream(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]], req.headers["content-type"], (errorMsg) => {
                        if (!errorMsg)
                            res.writeHeader("204", "No Content");
                        else
                            res.writeHeader("500", errorMsg);
                        res.end();
                    });
                    req.on('end', () => {
                        uploadRequest.end();
                    });
                    req.pipe(uploadRequest);
                } else {
                    res.writeHeader("404", "Channel Not Found");
                    res.end();
                }
            } else {
                res.writeHeader("400", "Bad Request");
                res.end();
            }
        } else if (req.urlObj.match(["api", "attachmentAction"])) {
            if (!req.urlObj.queryTokens.serviceId) {
                res.writeHeader("400", "Bad Request");
                res.end();
            } else {
                let serviceId = req.urlObj.queryTokens.serviceId[0],
                    ctx = res.chatContext.getUserContext(serviceId);

                if (ctx) {
                    let body = [];
                    req.on('data', (chunk) => { body.push(chunk); });
                    req.once('end', ()=> {
                        let payload;

                        try {
                            payload = JSON.parse(Buffer.concat(body).toString());
                        } catch(e) {
                            res.writeHeader("400", "Bad Request");
                            res.end();
                            return;
                        }
                        if (!ctx.sendAction(serviceId, payload, (result) => {
                                if (result !== false) {
                                    res.writeHeader(200);
                                    res.end(JSON.stringify(result));
                                } else {
                                    res.writeHeader(503, "Service unavailable");
                                    res.end();
                                }
                            })) {
                            // Error re-interpreting payload
                            res.writeHeader("400", "Bad Request");
                            res.end();
                        }
                    });
                } else {
                    res.writeHeader("404", "Service not found");
                    res.end();
                }
            }
            sessionManager.saveSession(req.session);
        } else if (req.urlObj.match(["api", "starChannel"])) {
            if (req.urlObj.queryTokens.room) {
                var ctx = res.chatContext.getChannelContext(req.urlObj.queryTokens.room[0]);
                if (!ctx) {
                    res.writeHeader("404", "Not Found");
                    res.end();
                } else if (!ctx.getChatContext().capacities.starChannel) {
                    res.writeHeader("400", "Bad Request");
                    res.end();
                } else {
                    ctx.starChannel(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]]);
                    res.writeHeader("204", "No Content");
                    res.end();
                }
            } else {
                res.writeHeader("400", "Bad Request");
                res.end();
            }
            sessionManager.saveSession(req.session);
        } else if (req.urlObj.match(["api", "unstarChannel"])) {
            if (req.urlObj.queryTokens.room) {
                var ctx = res.chatContext.getChannelContext(req.urlObj.queryTokens.room[0]);
                if (!ctx) {
                    res.writeHeader("404", "Not Found");
                    res.end();
                } else if (!ctx.getChatContext().capacities.starChannel) {
                    res.writeHeader("400", "Bad Request");
                    res.end();
                } else {
                    ctx.unstarChannel(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]]);
                    res.writeHeader("204", "No Content");
                    res.end();
                }
            } else {
                res.writeHeader("400", "Bad Request");
                res.end();
            }
            sessionManager.saveSession(req.session);
        } else if (req.urlObj.match(["api", "starMsg"])) {
            if (req.urlObj.queryTokens.room && req.urlObj.queryTokens.msgId) {
                var ctx = res.chatContext.getChannelContext(req.urlObj.queryTokens.room[0]);

                if (!ctx) {
                    res.writeHeader("404", "Not Found");
                    res.end();
                } else if (!ctx.getChatContext().capacities.starMsg) {
                    res.writeHeader("400", "Bad Request");
                    res.end();
                } else if (req.method === 'POST') {
                    ctx.starMsg(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]], req.urlObj.queryTokens.msgId[0]);
                    res.writeHeader("204", "No Content");
                    res.end();
                } else if (req.method === 'DELETE') {
                    ctx.unstarMsg(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]], req.urlObj.queryTokens.msgId[0]);
                    res.writeHeader("204", "No Content");
                    res.end();
                } else {
                    res.writeHeader("400", "Bad Request");
                    res.end();
                }
            } else {
                res.writeHeader("400", "Bad Request");
                res.end();
            }
        } else if (req.urlObj.match(["api", "pinMsg"])) {
            if (req.urlObj.queryTokens.room && req.urlObj.queryTokens.msgId) {
                var ctx = res.chatContext.getChannelContext(req.urlObj.queryTokens.room[0]);

                if (!ctx) {
                    res.writeHeader("404", "Not Found");
                    res.end();
                } else if (!ctx.getChatContext().capacities.starMsg) {
                    res.writeHeader("400", "Bad Request");
                    res.end();
                } else if (req.method === 'POST') {
                    ctx.pinMsg(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]], req.urlObj.queryTokens.msgId[0]);
                    res.writeHeader("204", "No Content");
                    res.end();
                } else if (req.method === 'DELETE') {
                    ctx.unpinMsg(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]], req.urlObj.queryTokens.msgId[0]);
                    res.writeHeader("204", "No Content");
                    res.end();
                } else {
                    res.writeHeader("400", "Bad Request");
                    res.end();
                }
            } else {
                res.writeHeader("400", "Bad Request");
                res.end();
            }
        } else if (req.urlObj.match(["api"])) {
            let knownVersion = (req.urlObj.queryTokens.v ? parseInt(req.urlObj.queryTokens.v[0], 10) : 0) || 0;
            res.chatContext.poll(knownVersion, req.reqT,
                    (knownVersion, cb) => {
                        accountConfigManager.fromAccountIdAndVersion(req.account.id, knownVersion, cb);
                    })
                .then((newData) => {
                    if (!res.ended) {
                        try {
                            res.writeHeader("200", {
                                "Content-Type": "application/json"
                            });
                            res.end(JSON.stringify(newData));
                        } catch (e) {}
                    }
                    sessionManager.saveSession(req.session);
                }).catch(() => {
                    if (!res.ended) {
                        try {
                            res.writeHeader("200", {
                                "Content-Type": "application/json"
                            });
                            res.end(JSON.stringify({
                                v: knownVersion
                            }));
                        } catch (e) {}
                    }
                    sessionManager.saveSession(req.session);
                });
        } else {
            srv.execTemplate(require('../template/_404.js'), req, res);
        }
    }
};
