var http = require("http")
    ,https = require("https")
    ,clientSession = require("client-sessions")

    ,Url = require("./url.js").Url
    ,config = require("../config.js")
    ,sessionManager = require("./session.js").SessionManager
    ,Slack = require("./slack.js").Slack
    ,slackManager = require("./slackManager.js").SlackManager
    ,FaviconWriter = require("./faviconWriter.js").FaviconWriter;

function Server(port) {
    var ctx = this;

    this.httpServ = http.createServer(function(req, res) {
        res.ended = false;
        res.on('end', () => {
            res.ended = true;
        });
        ctx.onRequest(req, res);
    });
    this.httpServ.listen(port, ctx.onListen);
}

Server.parseCookies = function(req) {
    var cookies = req.headers.cookie
    ,cookieObj = null;

    if (cookies) {
    cookieObj = {};
        cookies = cookies.split(";");
    for (var cookieIndex =0, nbCookies =cookies.length; cookieIndex < nbCookies; cookieIndex++) {
        var cookieParts = cookies[cookieIndex].indexOf("=")
        ,cookieKey = null
        ,cookieValue = null;

            if (cookieParts === -1) {
        cookieKey = cookies[cookieIndex].trim();
        } else {
        cookieKey = cookies[cookieIndex].substr(0, cookieParts).trim();
        cookieValue = cookies[cookieIndex].substr(cookieParts +1).trim();
        }
        cookieObj[cookieKey] = cookieValue;
    }
    }
    return cookieObj;
};

Server.stringifyCookies = function(cookies) {
    var cookieString = "";
    for (var cookieKey in cookies) {
    cookieString += cookieString;
    if (cookies[cookieString] !== null) {
        cookieString += "=";
    }
    cookieString += "; ";
    }
    return cookieString;
};

Server.prototype.onListen = function() {
    console.log("onListen");
};

function redirectToSlackAuth(res) {
    res.writeHeader("302", {
        Location: "https://slack.com/oauth/authorize"
        +"?client_id=" +config.clientId
        +"&scope=" +slackManager.getScope().join(",")
        +"&redirect_uri=" +config.rootUrl
    });
    res.end();
}

Server.prototype.onRequest = function(req, res) {
    req.reqT = Date.now();
    req.cookies = Server.parseCookies(req);
    req.session = sessionManager.forRequest(req);
    req.urlObj = new Url(req.url);

    if (!req.session || !req.session.slackToken) {
        if (req.urlObj.queryTokens.code) {
            Slack.getOauthToken(req.urlObj.queryTokens.code, config.rootUrl, (token) => {
                if (token) {
                    req.session = sessionManager.lazyForRequest(req);
                    req.session.setSlackToken(req.reqT, token);
                    res.writeHeader("302", {
                        Location: config.rootUrl
                    ,"Set-Cookie": "sessID="+req.session.sessId
                    });
                    sessionManager.saveSession(req.session);
                    res.end();
                } else {
                    redirectToSlackAuth(res);
                }
            });
        } else {
            redirectToSlackAuth(res);
        }
    } else if (req.urlObj.isPublic()) {
        if (!config.isDebug)
            res.setHeader('Cache-Control', 'private, max-age=' +15 * 60);
        res.setHeader('Content-Length', req.urlObj.serve.stat.size);
        res.writeHeader("200");
        req.urlObj.getReadStream().pipe(res, { end: true });
        sessionManager.saveSession(req.session);
        return; // async pipe will close when finished
    } else if (req.urlObj.match(["favicon.png"])) {
        if (!config.isDebug)
            res.setHeader('Cache-Control', 'private, max-age=' +15 * 60);
        var unreadHi = (req.urlObj.queryTokens.h && req.urlObj.queryTokens.h[0]) ? parseInt(req.urlObj.queryTokens.h[0], 10) : 0
            ,unread = (req.urlObj.queryTokens.m && req.urlObj.queryTokens.m[0]) ? parseInt(req.urlObj.queryTokens.m[0], 10) : 0;
        FaviconWriter.write(unreadHi, unread, (buf) => {
            res.writeHeader("200");
            res.end(buf);
        });
        sessionManager.saveSession(req.session);
        return;
    } else {
        // Api / dynamic content
        var apiSuccess = false;

        res.slack = slackManager.lazyGet(req.session, req.reqT);
        if (req.urlObj.match(["api", "hist"])) {
            if (!req.urlObj.queryTokens.room) {
                res.writeHeader("400", "Bad request");
            } else {
                var allFound = true;
                req.urlObj.queryTokens.room.forEach(function(targetId) {
                    if (!res.slack.data.getChannel(targetId)) {
                        allFound = false;
                    }
                    res.slack.fetchHistory(targetId);
                });
                if (allFound)
                    res.writeHeader("204", "No Content");
                else
                    res.writeHeader("404", "Channel not found");
            }
            sessionManager.saveSession(req.session);
            res.end();
        } else if (req.urlObj.match(["api", "typing"])) {
            if (!req.urlObj.queryTokens.room) {
                res.writeHeader("400", "Bad request");
                res.end();
            } else {
                var chan = res.slack.data.getChannel(req.urlObj.queryTokens.room[0]);

                if (!chan) {
                    res.writeHeader("404", "Chan not found");
                } else {
                    res.slack.sendTyping(chan);
                    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 {
                var chan = res.slack.data.getChannel(req.urlObj.queryTokens.room[0])
                    ,cmd = res.slack.data.commands.data['/' +req.urlObj.queryTokens.cmd[0]];

                if (!chan) {
                    res.writeHeader("404", "Chan not found");
                } else if (!cmd) {
                    res.writeHeader("404", "No such command");
                } else {
                    var args = req.urlObj.queryTokens.args ? req.urlObj.queryTokens.args[0] : "";
                    if (args === true)
                        args = "";
                    res.slack.sendCommand(chan, cmd, args);
                    res.writeHeader("204", "No Content");
                }
                res.end();
            }
        } else if (req.urlObj.match(["api", "markread"])) {
            if (!req.urlObj.queryTokens.room || !req.urlObj.queryTokens.ts) {
                res.writeHeader("400", "Bad request");
                res.end();
            } else {
                var chan = res.slack.data.getChannel(req.urlObj.queryTokens.room[0])
                    ,ts = parseFloat(req.urlObj.queryTokens.ts[0]);
                if (!chan)
                    res.writeHeader("404", "Chan Not Found");
                else if (isNaN(ts))
                    res.writeHeader("400", "Invalid date");
                else
                    res.slack.markRead(chan, ts);
                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 {
                var user = res.slack.data.getMember(req.urlObj.queryTokens.user[0]);
                if (!user) {
                    res.writeHeader("404", "User Not Found");
                    res.end();
                } else {
                    var url = user.icons.image_48;
                    if (url.substr(0, 7) === "http://")
                        http.get(url, (d) => {
                            d.pipe(res, { end: true });
                        });
                    else if (url.substr(0, 8) === "https://")
                        https.get(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 {
                    var chan = res.slack.data.getChannel(req.urlObj.queryTokens.room[0]);
                    if (chan) {
                        var attachments = null;
                        if (req.urlObj.queryTokens.attachments) {
                            try { attachments = JSON.parse(decodeURIComponent(req.urlObj.queryTokens.attachments[0])); }
                            catch (e) {}
                        }
                        if (req.urlObj.queryTokens.me)
                            res.slack.sendMeMsg(chan, req.urlObj.queryTokens.text);
                        else
                            res.slack.sendMsg(chan, req.urlObj.queryTokens.text, attachments);
                        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 {
                    var chan = res.slack.data.getChannel(req.urlObj.queryTokens.room[0]);
                    if (chan) {
                        res.slack.removeMsg(chan, 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 {
                    var chan = res.slack.data.getChannel(req.urlObj.queryTokens.room[0]);
                    if (chan) {
                        res.slack.editMsg(chan, 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"])) {
            var 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) {
                var chan = res.slack.data.getChannel(chanId);
                if (!chan) {
                    res.writeHeader("404", "Channel Not Found");
                } else if (req.method === 'POST') {
                    res.writeHeader("204", "No Content");
                    res.slack.addReaction(chan, msgId, reaction);
                } else if (req.method === 'DELETE') {
                    res.writeHeader("204", "No Content");
                    res.slack.removeReaction(chan, 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"]) {
                var chan = res.slack.data.getChannel(req.urlObj.queryTokens["room"][0]);
                if (chan) {
                    var uploadRequest = res.slack.openUploadFileStream(chan, 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"])) {
            res.slack.onRequest(
                (req.urlObj.queryTokens.v ? req.urlObj.queryTokens.v[0] : 0) || 0
                , (slack, newData) => {
                if (!res.ended) {
                    try {
                        if (!slack.connected) {
                            res.writeHeader("403", {
                                "Content-Type": "application/json"
                            });
                            res.end(slack.error);
                        } else {
                            res.writeHeader("200", {
                                "Content-Type": "application/json"
                            });
                            res.end(JSON.stringify(newData));
                        }
                    } catch (e) {}
                }
                sessionManager.saveSession(req.session);
            });
        } else {
            console.log(JSON.stringify(req.session));
            console.log(JSON.stringify(req.urlObj));
            console.log(JSON.stringify(req.cookies));
            res.writeHeader("404", "Not Found");
            res.end();
        }
    }
};

module.exports.HttpServ = Server;

