import jwt from "jsonwebtoken";
import config from "../config/app.config.js";
import { User, Role, Guest, ConfirmNum } from '../db/index.js';
import fs from "fs";
import CryptoJS from "crypto-js";
import validator from "validator";

// 현재 유저 상태 결정
const getUser = async (req, res) => {
    try {
        if (req.cookies.butterStudio) {
            const token = req.cookies.butterStudio;
            const decoded = jwt.verify(token, config.jwtSecret);
            res.json(decoded);
        } else {
            res.json({ id: 0, role: "user" });
        }
    } catch (error) {
        console.error(error);
        return res.status(500).send("유저를 가져오지 못했습니다.");
    }
}
// 로그인
const login = async (req, res) => {
    try {
        const { id, password } = req.body;
        //사용자 존재 확인
        const user = await User.scope("withPassword").findOne({ where: { userId: id } });
        if (!user) {
            return res.status(422).send(`사용자가 존재하지 않습니다`);
        }
        // 2) 비밀번호 확인은 데이터베이스 프로토타입 메소드에서 처리(사용자가 입력한 비밀번호와 서버에 있는 비번 비교)
        const passwordMatch = await user.comparePassword(password);
        if (passwordMatch) {
            // 3) 비밀번호가 맞으면 토큰 생성
            const userRole = await user.getRole();
            const signData = {
                id: user.id,
                role: userRole.name,
            };
            const token = jwt.sign(signData, config.jwtSecret, {
                expiresIn: config.jwtExpires,
            });
            // 4) 토큰을 쿠키에 저장
            res.cookie(config.cookieName, token, {
                maxAge: config.cookieMaxAge,
                path: "/",
                httpOnly: config.env === "production",
                secure: config.env === "production",
            });
            // 5) 사용자 반환
            res.json({
                id: user.id,
                role: userRole.name,
            });
        } else {
            // 6) 비밀번호 불일치
            res.status(401).send("비밀번호가 일치하지 않습니다");
        }
    } catch (error) {
        console.error(error);
        return res.status(500).send("로그인 에러");
    }
}
// 로그아웃
const logout = async (req, res) => {
    try {
        res.clearCookie(config.cookieName);
        res.json({
            id: 0,
            role: "user",
        })
        res.send('successfully cookie cleared.')
    } catch (error) {
        console.error(error);
        return res.status(500).send("로그인 에러");
    }
}

// 인증번호 발송
const confirmMbnum = async (req, res) => {
    try {
        
        // 휴대폰 인증
        const NCP_serviceID = process.env.NCP_serviceID;
        const NCP_accessKey = process.env.NCP_accessKey;
        const NCP_secretKey = process.env.NCP_secretKey;
        
        const date = Date.now().toString();
        const uri = NCP_serviceID;
        const accessKey = NCP_accessKey;
        const secretKey = NCP_secretKey;
        const method = 'POST';
        const space = " ";
        const newLine = "\n";
        const url = `https://sens.apigw.ntruss.com/sms/v2/services/${uri}/messages`;
        const url2 = `/sms/v2/services/${uri}/messages`;
        
        //시크릿 키를 암호화하는 작업
        const hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, secretKey);
        
        hmac.update(method);
        hmac.update(space);
        hmac.update(url2);
        hmac.update(newLine);
        hmac.update(date);
        hmac.update(newLine);
        hmac.update(accessKey);
        
        const hash = hmac.finalize();
        const signature = hash.toString(CryptoJS.enc.Base64);
        
        const phoneNumber = req.params.phone;
        console.log(phoneNumber);

        //인증번호 생성
        const verifyCode = Math.floor(Math.random() * (999999 - 100000)) + 100000;
        console.log("verifyCode : ", verifyCode);
        let today = new Date();
        let time = String(today.getTime());
        // let result = await axios({
        //     method: method,
        //     json: true,
        //     url: url,
        //     headers: {
        //         'Content-Type': "application/json",
        //         'x-ncp-apigw-timestamp': date,
        //         'x-ncp-iam-access-key': accessKey,
        //         'x-ncp-apigw-signature-v2': signature,
        //     },
        //     data: {
        //         type: 'SMS',
        //         contentType: 'COMM',
        //         countryCode: '82',
        //         from: '01086074580',
        //         content: `[본인 확인] 인증번호 [${verifyCode}]를 입력해주세요.`,
        //         messages: [
        //             {
        //                 to: `${phoneNumber}`,
        //             },
        //         ],
        //     },
        // });

        // const resultMs = result.data.messages;
        // console.log('resultMs', resultMs);

        // console.log('response', res.data, res['data']);
        const confirm = await ConfirmNum.findOne({ where: { phone: phoneNumber } });
        if (confirm) {
            await confirm.destroy();
            // 5분 유효시간 설정 
            await ConfirmNum.create({
                confirmNum: String(verifyCode),
                phone: phoneNumber,
                startTime: time,
            });
        } else {
            await ConfirmNum.create({
                confirmNum: String(verifyCode),
                phone: phoneNumber,
                startTime: time,
            }
            );
        }
        res.json({ startTime: time, isSuccess: true, code: 202, message: "본인인증 문자 발송 성공", result: res.data });
    } catch (error) {
        console.log("error: ", error);
        if (error.res == undefined) {
            res.json({ isSuccess: true, code: 200, message: "본인인증 문자 발송 성공", result: res.data });
        }
        else res.json({ isSuccess: true, code: 204, message: "본인인증 문자 발송에 문제가 있습니다.", result: error.res });
    }
};

//  인증번호 확인
const confirmNum = async (req, res) => {
    try {
        const { userMbnum, number, startTime } = req.body;
        const confirm = await ConfirmNum.findOne({ where: { phone: userMbnum, startTime: startTime } });

        let today = new Date();
        let time = today.getTime();
        const elapsedMSec = time - confirm.startTime;
        const elapsedMin = String(elapsedMSec / 1000 / 60);
        if (elapsedMin <= 5) {
            if (number !== confirm.confirmNum) {
                res.send("실패");
            } else {
                await confirm.destroy();
                res.send("성공");
            }
        } else {
            res.send("재전송")
        }
    } catch (error) {
        console.error("error : ", error.message);
        res.status(500).send("잘못된 접근입니다.");
    }
};

//유효성 검사
const validation = (errorMsg, data, minLength, maxLength, dataType) => {
    if (validator.isLength(data, minLength, maxLength)) {
        errorMsg[dataType] = false;
    } else {
        errorMsg[dataType] = true;
    }
    if (dataType === "userEmail") {
        if (validator.isEmail(data, minLength, maxLength)) {
            errorMsg[dataType] = false;
        } else {
            errorMsg[dataType] = true;
        }

    }
};
// 회원정보
const signup = async (req, res) => {
    const { userId, userName, userEmail, userNickName, userBirthday, userMbnum, userPassword } = req.body;
    try {
        let errorMsg = {
            errorId: false,
            errorName: false,
            errorEmail: false,
            errorBirthday: false,
            errorNickName: false,
            errorMbnum: false,
            errorPassword: false,
        };

        //유효성 검사
        validation(errorMsg, userId, 5, 10, "errorId");
        validation(errorMsg, userName, 1, 10, "errorName");
        validation(errorMsg, userEmail, 3, 20, "errorEmail");
        validation(errorMsg, userBirthday, 6, 6, "errorBirthday");
        validation(errorMsg, userNickName, 1, 10, "errorNickName");
        validation(errorMsg, userMbnum, 11, 11, "errorMbnum");
        validation(errorMsg, userPassword, 8, 11, "errorPassword");

        let valid = !(Object.values(errorMsg).some((element) => (element)));
        // db에서 데이터 중복검사
        const id = await User.findOne({ where: { userId: userId } });
        const mbnum = await User.findOne({ where: { phoneNumber: userMbnum } });
        const email = await User.findOne({ where: { email: userEmail } });
        if (!valid) {
            res.json(errorMsg);
        } else {
            if (id) {
                return res.status(401).send(`이미 있는 아이디입니다.`);
            } else if (email) {
                return res.status(401).send(`이미 있는 이메일입니다.`);
            } else if (mbnum) {
                return res.status(401).send(`이미 있는 휴대폰번호입니다.`);
            } else{
                const role = await Role.findOne({ where: { name: "member" } })
                await User.create({
                    userId: userId,
                    name: userName,
                    email: userEmail,
                    nickname: userNickName,
                    birth: userBirthday,
                    phoneNumber: userMbnum,
                    password: userPassword,
                    img: "",
                    roleId: role.id
                });
                res.json("성공");
            }
        }
    } catch (error) {
        console.error(error.message);
        res.status(500).send("회원가입 에러. 나중에 다시 시도 해주세요");
    }
};

const getMember = async (req, res) => {
    try {
        const token = req.cookies.butterStudio;
        const decoded = jwt.verify(token, config.jwtSecret);
        if (decoded.role === "member") {
            const user = await User.findOne({ where: { id: decoded.id } });
            res.json({ nickname: user.nickname, img: user.img });
        } else {
            res.status(500).send("잘못된 접근입니다.");
        }
    } catch (error) {
        console.error("error : ", error.message);
        res.status(500).send("잘못된 접근입니다.");
    }
}
// 프로필 변경
const uploadProfile = async (req, res) => {
    try {
        const image = req.file.filename;
        const token = req.cookies.butterStudio;
        const decoded = jwt.verify(token, config.jwtSecret);

        if (decoded) {
            const img = await User.findOne({ where: { id: decoded.id }, attributes: ["img"] });
            fs.unlink("upload" + `\\${img.img}`, function (data) { console.log(data); });

            const user = await User.update({
                img: image
            }, { where: { id: decoded.id } });
            if (user) {
                const success = await User.findOne({ where: { id: decoded.id }, attributes: ["img"] });
                res.json(success)
            } else {
                throw new Error("프로필 등록 실패")
            }
        }
    } catch (error) {
        console.error(error.message);
        res.status(500).send("프로필 에러");
    }
}
// 기본 비밀번호인지 확인
const comparePw = async (req, res) => {
    try {
        //쿠키 안 토큰에서 id추출
        const token = req.cookies.butterStudio;
        const decoded = jwt.verify(token, config.jwtSecret);
        //해당 id의 행 추출
        const user = await User.scope("withPassword").findOne({ where: { id: decoded.id } });
        //입력한 비번과 해당 행 비번을 비교
        const passwordMatch = await user.comparePassword(req.params.pw);
        //클라이언트로 동일여부를 전송
        if (passwordMatch) {
            return res.json(true)
        } else {
            return res.json(false)
        }
    } catch (error) {
        console.error("error : ", error.message);
        res.status(500).send("인증 에러");
    }
}
// 회원정보 수정할 때 쓰는 함수
const overlap = async (decoded, dataType, data) => {
    try {
        let overlap = await User.findOne({ where: { id: decoded.id } });
        // 변경할 데이터가 자기자신이면 true
        if (overlap[dataType] === data) {
            return true
        } else {
            // 그렇지 않으면 다른 데이터들 중에서 중복되는지 검사
            let overlap2 = await User.findOne({ attributes: [dataType] });
            if (overlap2[dataType] === data) {
                return false
            } else {
                return true
            }
        }
    } catch (error) {
        console.error(error.message);
    }
}
// 회원정보 수정
const modifyUser = async (req, res) => {
    try {
        const token = req.cookies.butterStudio;
        const decoded = jwt.verify(token, config.jwtSecret);
        const { userName, userEmail, userNickName, userMbnum, userPassword } = req.body;

        let errorMsg = {
            errorName: false,
            errorEmail: false,
            errorNickName: false,
            errorMbnum: false,
            errorPassword: false,
        };

        //유효성 검사
        validation(errorMsg, userName, 1, 10, "errorName");
        validation(errorMsg, userEmail, 3, 20, "errorEmail");
        validation(errorMsg, userNickName, 1, 10, "errorNickName");
        validation(errorMsg, userMbnum, 11, 11, "errorMbnum");
        validation(errorMsg, userPassword, 8, 11, "errorPassword");

        let valid = !(Object.values(errorMsg).some((element) => (element)));
        const overlapEmail = await overlap(decoded, "email", userEmail);
        const overlapMbnum = await overlap(decoded, "phoneNumber", userMbnum);
        if (!valid) {
            res.json(errorMsg);
        } else {
            if (overlapEmail && overlapMbnum) {
                await User.update({
                    name: userName,
                    email: userEmail,
                    nickname: userNickName,
                    phoneNumber: userMbnum,
                    password: userPassword,
                }, { where: { id: decoded.id }, individualHooks: true });
                res.json("성공");
            } else if (!overlapEmail && overlapMbnum) {
                res.status(500).send("이미 있는 이메일입니다.");
            } else if (overlapEmail && !overlapMbnum) {
                res.status(500).send("이미 있는 핸드폰번호입니다.");
            } else {
                res.status(500).send("이미 있는 이메일, 핸드폰번호입니다.");
            }
        }
    } catch (error) {
        console.error(error.message);
        res.status(500).send("수정 에러. 나중에 다시 시도 해주세요");
    }
};

const getUserInfo = async (req, res) => {
    const { id } = req.body
    console.log(id)
    try {
        const userInfo = await User.findOne({
            where: { id: id },
            attributes: ["id", "userId", "email", "nickname", "birth", "phoneNumber"]
        })
        console.log(userInfo)
        res.json(userInfo)
    } catch (error) {
        res.status(500).send("회원정보 불러오기 실패");
    }
}

const saveGuestInfo = async (req, res) => {
    const { name, email, birth, phoneNumber, password } = req.body
    try {
        const newGuest = await Guest.create({
            name: name,
            email: email,
            birth: birth,
            phoneNumber: phoneNumber,
            password: password,
        });
        // console.log(newGuest)
        res.clearCookie(config.cookieName);
        const token = jwt.sign({id: newGuest.id, role: "user"}, config.jwtSecret, {
            expiresIn: config.jwtExpires,
        });
        res.cookie(config.cookieName,token , {
            maxAge: config.cookieMaxAge,
            path: "/",
            httpOnly: config.env === "production",
            secure: config.env === "production",
        })
        res.json(newGuest);
    } catch (error) {
        res.status(500).send("비회원정보 등록 실패");
    }
}

const getGuestInfo = async (req,res) => {
    const {guestId} = req.params
    // console.log(req.body)
    try {
        const guestInfo = await Guest.findOne({
            where: {
                id:guestId
            }
        })
        // console.log("guestInfo====", guestInfo)
        res.json(guestInfo)
    } catch (error) {
        res.status(500).send("비회원정보 불러오기 실패");
    }
}
export default {
    getUser,
    login,
    logout,
    confirmMbnum,
    confirmNum,
    signup,
    comparePw,
    modifyUser,
    saveGuestInfo,
    getMember,
    uploadProfile,
    getUserInfo,
    getGuestInfo
}
