
const User = require("../models/mongooseSchemas/User");

const router = require("express").Router();
const pug = require("pug");
const passport = require("passport");
const LocalStrategy = require('passport-local').Strategy;
const multer = require("multer");
const dataRender = require("../models/DataRender");

// 序列化: 在第一次驗證之後，session皆不會保留驗證訊息，因此取得user.id
passport.serializeUser(function(user, done) {
    done(null, user._id);
});

// 反序列化: 用user.id來取得資料庫中的指定資料
passport.deserializeUser(function(id, done) {
    User.findById(id, function(err, user) {
        done(err, user);
    });
});

/**
 * 定義在「登入」頁面，使用者進行登入、傳送帳號密碼時所做的驗證策略。
 */
passport.use("login" ,new LocalStrategy({
    usernameField: "username",
    passwordField: "password"
}, function (username, password, done) {
    User.AccountComparison(username, password, (err, user) => {
        if (err) return done(err);
        if (!user)
            return done(null, false, {message: "使用者名稱或密碼錯誤。"});
        else
            return done(null, user);
    });
}));

/**
 * 頁面「首頁」的路由處理。
 */
router.use(require("./index"));

/**
 * 頁面「傑作藝廊」的路由處理。
 */
router.use(require("./gallery"));

/**
 * 頁面「畫作主題」的路由處理。
 */
router.use(require("./theme"));

/**
 * 頁面「繪圖創作」的路由處理。
 */
router.use(require("./drawing"));

/**
 * 頁面「意見回饋」的路由處理。
 */
router.use(require("./feedback"));

/**
 * 註冊、登入與登出的相關頁面與處理。
 */
router.use(require("./gate"));

/**
 * 頁面「展示藝廊」的路由處理。
 */
router.use(require("./showcase"));

/**
 * 頁面「投稿主題」的路由處理。.
 */
router.use(require("./submit_theme"));

/**
 * 頁面「」
 */
router.use(require("./vote_theme"));

/* =================================== */

/**
 * 頁面「個人主頁」的路由處理。
 */
router.get("/home/:username", (req, res) => {
    // 若使用者有登入，則允許觀看其他會員的個人頁面
    if (req.session.passport && req.session.passport.user) {
        dataRender.DataRender("personal_page", req.url, req.session, (err, dataObj) => {
            if (!err) {
                res.render("personal_page", dataObj);
            }
            else if (User.IsUserNotExist(err)) {
                res.redirect("/homenotexist/" + req.params["username"]);
            }
            else {
                res.setHeader("Content-Type", "text/plain");
                res.status(500);
                res.end("Server side error 500 : " + err);
            }
        });
    }
    // 若沒有則轉跳到登入頁面
    else {
        res.redirect("/login");
    }
});

/**
 * 在「個人頁面」要尋找指定使用者之下，發生找不到該使用者時所跳轉的訊息頁面。
 */
router.get("/homenotexist/:username", (req, res) => {
    dataRender.DataRender("message_form", req.url, req.session, (err, dataObj) => {
        if (err) {
            res.setHeader("Content-Type", "text/plain");
            res.status(500);
            res.end("Server side error 500 : " + err);
        }
        else {
            res.render("message_form", dataObj);
        }
    });
});

/**
 * 頁面「編輯個人資料」的路由處理。
 */
router.get("/edit_personal_info", (req, res) => {
    // 若使用者有登入，則將編輯頁面傳送至客戶端
    if (req.session.passport && req.session.passport.user) {
        // 取得相對應的插值物件
        dataRender.DataRender("edit_personal_info", req.url, req.session, (err, dataObj) => {
            // 若發生錯誤則傳送訊息。
            if (err) {
                res.setHeader("Content-Type", "text/plain");
                res.status(500);
                res.end("Server side error 500 : " + err);
            }
            // 若無則將頁面傳送至客戶端
            else {
                res.render("edit_personal_info", dataObj);
            }
        });
    }
    // 若沒有則轉跳到首頁
    else {
        res.redirect("/");
    }
});

/**
 * 檔案上傳協助物件，SavePersonalInfo_Upload。
 * 上傳的圖片會暫存在"./temp"中。
 */
const SPI_Upload = multer({ 
    dest: "./temp",
    limits: { fileSize: 131072, files: 1 }
});

/**
 * 在頁面「編輯個人資料」之下，按下「儲存更變」後所傳來的資料，由此路由來處理。
 * 圖像檔案資訊存放於「req.files」中，文字類訊息存放於「req.body」中。
 */
router.post("/save_personal_info", SPI_Upload.single("photo"), (req, res) => {
    // 若沒有登入，則跳轉至首頁
    if (!req.session.passport || !req.session.passport.user) return res.redirect("/");

    res.setHeader("Content-Type", "application/json");

    let theFile = req.file;
    // 若有登入，則在做檢查動作。
    // 若使用者有傳送檔案 且 檔案類別不為"image/jpeg"與"image/png"的話，則回送錯誤訊息。
    if (theFile && theFile.mimetype != "image/jpeg" && theFile.mimetype != "image/png") {
        res.end({"isOK": false, "field": "photo", "message": "類型錯誤: 上傳的檔案類型請選擇jpg或png圖檔。"});
        return;
    }

    // 檢查「姓」
    req.checkBody("lastName")
       .notEmpty()
       .withMessage("「姓」為必要輸入的欄位，請輸入您的大名。")
       .matches(/^[a-zA-Z\u2E80-\u2FDF\u3190-\u319F\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF]{1,16}$/)
       .withMessage("「姓」欄位輸入錯誤，請輸入小於17字的英文或中文字。");
    // 檢查「名」
    req.checkBody("firstName")
       .notEmpty()
       .withMessage("「名」為必要輸入的欄位，請輸入您的大名。")
       .matches(/^([a-zA-Z\u2E80-\u2FDF\u3190-\u319F\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF]{1,16})([\ ]*)([a-zA-Z]{0,16})$/)
       .withMessage("「名」欄位輸入錯誤，請輸入小於33字的英文或中文字。");
    // 檢查「暱稱」
    req.checkBody("nickname")
       .len({max: 16})
       .withMessage("暱稱欄位的字數至多16字元。")
       .matches(/^([^<>\&"']{0,16})$/)
       .withMessage("在暱稱欄位中請勿輸入非法字元: <>&\"\'");
    // 檢查「短言」
    req.checkBody("motto")
       .len({max: 32})
       .withMessage("短言欄位的字數至多32字元。")
       .matches(/^([^<>\&"']{0,32})$/)
       .withMessage("在短言欄位中請勿輸入非法字元: <>&\"\'");
    
    // 取得檢查結果
    req.getValidationResult().then((result) => {
        let errors = result.mapped();                   // 將結果做成字典物件
        let responseMsg = {isOK: result.isEmpty()};     // 宣告、初步定義回應物件

        // 若檢查通過，則將資料更新至資料庫(交給資料庫Schema處理)
        if (responseMsg.isOK) {

            // 呼叫UpdatePersonalInfo，傳入使用者的_id、文字資料、檔案訊息與回呼函式
            User.UpdatePersonalInfo(req.session.passport.user, req.body, theFile, (err, isOK) => {
                // 若有錯誤，則將錯誤設定至回應物件上，並在控制台上印出錯誤
                if (err) {
                    responseMsg.isOK = false;
                    responseMsg.field = "SERVER";
                    responseMsg.message = (User.IsUserNotExist(err) ? "使用者資料對應錯誤。請重新登入再執行此操作。" : "伺服器內部錯誤，請稍後再嘗試。");
                    console.log(err);
                }
                // 若無錯誤，則標上 personalInfoUpdated 與轉跳頁面
                else {
                    req.session.personalInfoUpdated = true;
                    responseMsg.redirect = "/personalinfo_updated";
                }
                // 若無錯誤，則直接回呼 (不改變 responseMsg.isOK 的 true 值)
                res.send(responseMsg);
            });
        }
        else {
            let firstErr = Object.values(errors)[0];    // 取得第一個錯誤訊息物件
            responseMsg.field = firstErr.param;         // 將錯誤的欄位名稱新增到回應物件的field屬性
            responseMsg.message = firstErr.msg;         // 將錯誤訊息新增到回應物件的message屬性
            res.send(responseMsg);
        }
    });
});

/**
 * 在「編輯個人資料」頁面下，使用者上傳資料並成功後的跳轉訊息頁面。
 */
router.get("/personalinfo_updated", (req, res) => {
    // 若有標記 personalInfoUpdated ，
    if (req.session.personalInfoUpdated) {

        // 刪除標記，以免重複
        delete req.session.personalInfoUpdated;

        // 取得插值資料(傳入URL，給內部判斷)
        dataRender.DataRender("message_form", req.url, req.session, (err, dataObj) => {
            // 若有錯誤，則發送錯誤訊息
            if (err) {
                res.setHeader("Content-Type", "text/plain");
                res.status(500);
                res.end("Server side error 500 : " + err);
            }
            else {
                res.render("message_form", dataObj);
            }
        });
    }
    // 若無標記，則直接跳轉到首頁
    else {
        res.redirect("/");
    }
});

/**
 * 頁面「撰寫站內訊息」的路由處理
 */
router.get(["/write_message", "/write_message/:username"], (req, res) => {
    if (req.session.passport && req.session.passport.user) {
        dataRender.DataRender("write_message", req.url, req.session, (err, dataObj) => {
            if (err) {
                console.log(err);
                res.setHeader("Content-Type", "text/plain");
                res.status(500);
                res.end("Server side error 500 : " + err);
            }
            else {
                res.render("write_message", dataObj);
            }
        });
    }
    else {
        res.redirect("/");
    }
});

/**
 * 取得由「撰寫站內訊息」頁面所傳來的訊息
 */
router.post("/write_message", (req, res) => {
    res.setHeader("Content-Type", "application/json");
    // 檢查使用者是否有登入。 若有登入，則近一步地做檢查動作
    if (req.session.passport && req.session.passport.user) {
        // 檢查「收件者」
            req.checkBody("recipient")
            .notEmpty()
            .withMessage("請選擇接收此站內信的目標使用者名稱。");
        
        // 檢查「標題」
        req.checkBody("subject")
            .notEmpty()
            .withMessage("請在「主旨」欄位上輸入1~32字間的主旨。")
            .matches(/^([^<>\&"'$]{1,32})$/)
            .withMessage("請勿在「主旨」中輸入有包含「<>&\"'$」有關的字元。");
        
        // 檢查「內文」
        req.checkBody("content")
            .notEmpty()
            .withMessage("請在「內文」中輸入您想對目標使用者傳達的訊息。")
            .len({min: 8, max: 500})
            .withMessage("請在「內文」中輸入8~500數量的文字訊息。");

        // 取得驗證結果
        req.getValidationResult().then((result) => {
            let errors = result.mapped();
            let responseMsg = { isOK: result.isEmpty() };
            // 若驗證不通過，則傳送錯誤訊息
            if (!responseMsg.isOK) {
                let firstErr = Object.values(errors)[0];
                responseMsg.field = firstErr.param;
                responseMsg.message = firstErr.msg;
                res.send(responseMsg);
                return;
            }
            
            // 若驗證皆通過，則寄發信件: 將寄件者的信件寄給收件者。
            User.SendSiteMail(req.session.passport.user, req.body, (err, isOK) => {
                if (!err) {
                    responseMsg.url = "/send_sitemail_successfully";
                }
                else if (User.IsUserNotExist(err)) {
                    responseMsg.isOK = false;
                    responseMsg.field = "SERVER";
                    responseMsg.message = "找不到指定的目標的收件者。";
                }
                else {
                    responseMsg.isOK = false;
                    responseMsg.field = "SERVER";
                    responseMsg.message = err.message;
                }
                res.send(responseMsg);
            });
        });
    }
    // 若沒有登入，則回應錯誤。
    else {
        res.send({isOK: false, message: "您尚未登入，請登入後再嘗試撰寫、傳送站內訊息。"});
    }
});

/**
 * 在頁面「撰寫站內訊息」下，成功處理站內信後的跳轉頁面。
 */
router.get("/send_sitemail_successfully", (req, res) => {
    dataRender.DataRender("message_form", req.url, req.session, (err, dataObj) => {
        if (err) {
            res.setHeader("Content-Type", "text/plain");
            res.status(500);
            res.end("Server side error 500 : " + err);
        }
        else {
            res.render("message_form", dataObj);
        }
    });
});

/**
 * 頁面「更變密碼」的路由處理。
 */
router.get("/newpw", (req, res) => {
    // 確認使用者是否有登入
    if (req.session.passport && req.session.passport.user) {
        dataRender.DataRender("change_password", req.url, req.session, (err, dataObj) => {
            if (err) {
                res.setHeader("Content-Type", "text/plain");
                res.status(500);
                res.end("Server side error 500 : " + err);
            }
            else {
                let errMsg = req.flash("error");
                if (errMsg.length > 0) {
                    dataObj.datas.errorMessage = errMsg[0];
                }
                res.render("change_password", dataObj);
            }
        });
    }
    // 若沒有登入，則轉跳到首頁。
    else {
        res.redirect("/");
    }
})

/**
 * 在頁面「更變密碼」下，傳送新、舊密碼來進行更新密碼的處理。
 */
router.post("/newpw", (req, res) => {
    // 若使用者為登入狀態，則進一步檢查內容
    if (req.session.passport && req.session.passport.user) {
        let body = req.body;
        req.checkBody("oldpw")
           .notEmpty()
           .withMessage("「舊密碼」為必要的欄位，請填寫您目前正使用的密碼。");
        
        req.checkBody("newpw")
           .notEmpty()
           .withMessage("「新密碼」為必要的欄位，請填寫您想要的新密碼。")
           .not().equals(body.oldpw)
           .withMessage("「新密碼」請勿與「舊密碼」完全一致。請更換新密碼。")
           .matches(/(^[0-9a-zA-Z_?!@#+-]{5,16}$)/)
           .withMessage("「新密碼」欄位僅能填寫5~16的數字、英文字母或「_?!@#+-」字元。");
        
        req.checkBody("newpw_confirm")
           .notEmpty()
           .withMessage("「確認新密碼」為必要的欄位，請填寫與「新密碼」欄位中一致的密碼。")
           .equals(body.newpw)
           .withMessage("「確認新密碼」欄位必須與「新密碼」欄位中的密碼一致。");

        // 取得驗證結果
        req.getValidationResult().then((result) => {

            // 如果結果為空，也就是沒有任何錯誤訊息的話
            if (result.isEmpty()) {
                // 嘗試更變使用者的密碼
                User.ChangePassword(req.session.passport.user, body.oldpw, body.newpw, (err, result) => {
                    // 若有錯誤，則對錯誤做處理
                    if (err) {
                        if (User.IsUserNotExist(err)){
                            // * 可改成轉跳到登入頁面 *
                            req.flash("error", "使用者帳號已登出，請先重新登入。");
                            res.redirect("/newpw");
                        }
                        // 若為其他錯誤，則告知使用者
                        else {
                            console.log(err);
                            req.flash("error", "伺服器內部錯誤，請稍後再嘗試。");
                            res.redirect("/newpw");
                        }
                    }
                    // 若無錯誤，則依照結果發送訊息
                    else {
                        if (result) {
                            req.session.changePW_successfuly = true;
                            res.redirect("/newpw_success");
                        }
                        else {
                            req.flash("error", "「舊密碼」輸入錯誤。請輸入正確的、當前使用的密碼。");
                            res.redirect("/newpw");
                        }
                    }
                });
            }
            // 若不為空，表示有誤
            else {
                let firstErr = Object.values(result.mapped())[0];   // 取得第一個錯誤
                req.flash("error", firstErr.msg);                   // 將錯誤設定至flash中
                res.redirect("/newpw");                             // 轉跳到「更新密碼」頁面
            }
        });
    }
    // 若沒有登入，則轉跳到首頁。 (暫時)
    else {
        res.redirect("/");
    }
});

/**
 * 在頁面「更變密碼」之下，成功更改密碼之後的轉跳訊息頁面。
 */
router.get("/newpw_success", (req, res) => {
    // 確認是否「剛剛更新完密碼」
    if (req.session.changePW_successfuly) {
        delete req.session.changePW_successfuly;    // 刪除標記，以免重複

        dataRender.DataRender("message_form", req.url, req.session, (err, dataObj) => {
            if (err) {
                res.setHeader("Content-Type", "text/plain");
                res.status(500);
                res.end("Server side error 500 : " + err);
            }
            else {
                res.render("message_form", dataObj);
            }
        });
    }
});



module.exports = router;