const argon2 = require('argon2');
const jwt = require('jsonwebtoken');

const AccountConfig = require('../../config/account');
const sequelize = require('../../config/database');
const User = require('../models/User');
const Profile = require('../models/Profile');
const Crew = require('../models/Crew');
const Event = require('../models/Event');
const UserCrew = require('../models/UserCrew');
const EventParticipants = require('../models/EventParticipants');
const {buildFullImageUrl, removeHostFromImageUrl} = require('./imageController');

const authTokenExpiryTime = '1h';
const listItemsPerPage = 10;

async function hashPassword(password) {
    return await argon2.hash(password, {
        type: argon2.argon2id,
    });
}

async function verifyPassword(hash, plainPassword) {
    return await argon2.verify(hash, plainPassword);
}

// JWT 생성
function generateToken(payload) {
    return jwt.sign(
        payload,
        AccountConfig.JWT_SECRET_KEY,
        {
            expiresIn: authTokenExpiryTime,
        },
    );
}

exports.signUp = async (req, res) => {
    const {name, email, password} = req.body;
    if (!name) {
        return res.status(400).json({error: '이름을 입력하세요.'});
    }

    if (!email) {
        return res.status(400).json({error: '이메일을 입력하세요.'});
    }

    if (!password) {
        return res.status(400).json({error: '비밀번호를 입력하세요.'});
    }

    // Check if the user already exists
    const user = await User.findOne({
        where: {email: email},
    });

    if (user) {
        return res.status(409).json({error: '이미 등록된 사용자입니다.'});
    }

    const profile = req.body.profile || {};

    if (!profile.regionID) {
        return res.status(400).json({error: '지역을 입력하세요.'});
    }

    if (!profile.sportTypeID) {
        return res.status(400).json({error: '운동 종목을 입력하세요.'});
    }

    try {
        const tx = await sequelize.transaction();

        try {
            const user = await User.create({
                name,
                email,
                password: await hashPassword(password),
            }, {transaction: tx});

            const userProfile = await Profile.create({
                profileImage: profile.profileImage,
                regionID: profile.regionID,
                job: profile.job || null,
                birthDate: profile.birthDate || null,
                experience: profile.experience || null,
                introduction: profile.introduction || null,
                sportTypeID: profile.sportTypeID,
                userID: user.userID,
            }, {transaction: tx});

            await tx.commit();

            // 생성된 사용자 정보 반환
            res.status(201).json({
                userID: user.userID,
                name: user.name,
                email: user.email,
                createdAt: user.createdAt,
                updatedAt: user.updatedAt,
                profile: {
                    ...userProfile.toJSON(),
                    profileImage: buildFullImageUrl(userProfile.profileImage),
                },
            });
        } catch (error) {
            await tx.rollback();
            console.error('사용자 생성 중 오류:', error);
            res.status(500).json({error: '사용자 생성 중 오류가 발생했습니다.'});
        }
    } catch (error) {
        console.error('사용자 생성 중 오류:', error);
        res.status(500).json({error: '사용자 생성 중 오류가 발생했습니다.'});
    }
}

exports.verifyEmail = async (req, res) => {
    const {email} = req.body;

    if (!email) {
        return res.status(400).json({error: '이메일을 입력하세요.'});
    }

    const user = await User.findOne({
        where: {email: email},
    });

    if (user) {
        return res.status(409).json({
            error: '이미 등록된 사용자입니다.'
        });
    }

    res.status(200).json({
        message: '사용 가능한 이메일입니다.'
    });
}

exports.login = async (req, res) => {
    try {
        const {email, password} = req.body;

        if (!email) {
            return res.status(400).json({error: '이메일을 입력하세요.'});
        }

        if (!password) {
            return res.status(400).json({error: '비밀번호를 입력하세요.'});
        }

        // 이메일로 사용자 조회
        const user = await User.findOne({
            where: {email: email},
        });

        // 사용자가 없을 경우
        if (!user) {
            return res.status(404).json({error: '사용자를 찾을 수 없습니다.'});
        }

        // 비밀번호 검증
        const isPasswordMatch = await verifyPassword(user.password, password);

        // 비밀번호가 일치하지 않을 경우
        if (!isPasswordMatch) {
            return res.status(401).json({error: '비밀번호가 일치하지 않습니다.'});
        }

        // JWT 토큰 생성
        const token = await generateToken({
            userID: user.userID,
            email: user.email,
        });

        // 토큰 반환
        res.status(200).json({
            "auth_token": token,
        });
    } catch (error) {
        console.error('로그인 중 오류:', error);
        res.status(500).json({error: '로그인 중 오류가 발생했습니다.'});
    }
}

exports.getUserById = async (req, res) => {
    try {
        const userID = req.params.id;

        const user = await User.findByPk(userID, {
            include: [{model: Profile}],
            attributes: ['userID', 'name', 'email', 'createdAt', 'updatedAt'],
        });

        if (!user) {
            return res.status(404).json({error: '사용자를 찾을 수 없습니다.'});
        }

        res.status(200).json({
            userID: user.userID,
            name: user.name,
            email: user.email,
            createdAt: user.createdAt,
            updatedAt: user.updatedAt,
            profile: {
                ...user.Profile.toJSON(),
                profileImage: buildFullImageUrl(user.Profile.profileImage),
            },
        });
    } catch (error) {
        console.error('사용자 조회 중 오류:', error);
        res.status(500).json({error: '사용자 조회 중 오류가 발생했습니다.'});
    }
}


exports.updateUser = async (req, res) => {
    // TODO: verify the user by auth token with middleware. #14

    const userID = req.user.userID;

    if (parseInt(req.params.id) !== parseInt(userID)) {
        return res.status(403).json({
            error: '사용자 정보를 업데이트할 권한이 없습니다.'
        });
    }

    const tx = await sequelize.transaction();

    const {password, profile} = req.body;

    try {
        const user = await User.findByPk(userID, {transaction: tx});

        if (!user) {
            await tx.rollback();
            return res.status(404).json({error: '사용자를 찾을 수 없습니다.'});
        }

        if (password) user.password = await hashPassword(password);

        await user.save({transaction: tx});

        let userProfile;
        if (profile) {
            userProfile = await user.getProfile({transaction: tx});
            if (userProfile) {
                // TODO: Save the image to storage and save the URL to the database
                if (profile.profileImage) userProfile.profileImage = removeHostFromImageUrl(profile.profileImage);
                if (profile.regionID) userProfile.regionID = profile.regionID;
                if (profile.job) userProfile.job = profile.job;
                if (profile.birthDate) userProfile.birthDate = profile.birthDate;
                if (profile.experience) userProfile.experience = profile.experience;
                if (profile.introduction) userProfile.introduction = profile.introduction;
                if (profile.sportTypeID) userProfile.sportTypeID = profile.sportTypeID;

                await userProfile.save({transaction: tx});
            }
        }
        await tx.commit();

        res.status(200).json({
            userID: user.userID,
            name: user.name,
            email: user.email,
            createdAt: user.createdAt,
            updatedAt: user.updatedAt,
            profile: {
                ...userProfile.toJSON(),
                profileImage: buildFullImageUrl(userProfile.profileImage),
            },
        });
    } catch (error) {
        await tx.rollback();
        console.error('사용자 업데이트 중 오류:', error);
        res.status(500).json({error: '사용자 업데이트 중 오류가 발생했습니다.'});
    }
}

exports.deleteUser = async (req, res) => {
    const userID = req.user.userID;

    if (parseInt(req.params.id) !== parseInt(userID)) {
        return res.status(403).json({
            error: '사용자 정보를 삭제할 권한이 없습니다.'
        });
    }

    const tx = await sequelize.transaction();

    try {
        const user = await User.findByPk(userID, {transaction: tx});

        if (!user) {
            await tx.rollback();
            return res.status(404).json({error: '사용자를 찾을 수 없습니다.'});
        }

        const userProfile = await user.getProfile({transaction: tx});
        if (userProfile) {
            await userProfile.destroy({transaction: tx});
        }

        await user.destroy({transaction: tx});
        await tx.commit();
        res.status(204).json({message: '사용자가 삭제되었습니다.'});
    } catch (error) {
        await tx.rollback();
        console.error('사용자 삭제 중 오류:', error);
        res.status(500).json({error: '사용자 삭제 중 오류가 발생했습니다.'});
    }
}

exports.listUserCrews = async (req, res) => {
    const userID = req.user.userID;

    const page = req.query.page || 0;

    try {
        const user = await User.findByPk(userID);

        if (!user) {
            return res.status(404).json({error: '사용자를 찾을 수 없습니다.'});
        }

        const crews = await UserCrew.findAll({
            where: { userID: userID },
            include: [
                {
                    model: Crew,
                    attributes: [
                        'crewID',
                        'name',
                        'createdDate',
                        'regionID',
                        'sportTypeId',
                        'capacity',
                        'fee_krw',
                        'description',
                    ],
                    include: [
                        {
                            model: UserCrew,
                            attributes: [],
                        },
                    ],
                },
            ],
            attributes: {
                include: [
                    [
                        sequelize.literal(`(
                          SELECT COUNT(*)
                          FROM UserCrew AS uc
                          WHERE uc.crewID = UserCrew.crewID
                        )`),
                        'memberCount',
                    ],
                ],
            },
            limit: listItemsPerPage,
            offset: parseInt(page, 10) * listItemsPerPage,
            order: [[sequelize.literal('crewID'), 'DESC']],
        });

        // NOTE: cache it.
        const joinedCrewCount = await UserCrew.count({
            where: {userID: userID},
        });

        res.status(200).json({
            'crews': crews,
            'total': joinedCrewCount,
            'per_page': listItemsPerPage,
        });
    } catch (error) {
        console.error('사용자 크루 조회 중 오류:', error);
        res.status(500).json({error: '사용자 크루 조회 중 오류가 발생했습니다.'});
    }
}

exports.listUserEvents = async (req, res) => {
    const userID = req.user.userID;

    const page = req.query.page || 0;

    console.log(userID);

    try {
        const user = await User.findByPk(userID);

        if (!user) {
            return res.status(404).json({error: '사용자를 찾을 수 없습니다.'});
        }

        const events = await EventParticipants.findAll({
            where: { userID: userID },
            include: [
                {
                    model: Event,
                    attributes: [
                        'eventID',
                        'name',
                        'eventDate',
                        'regionID',
                        'sportTypeId',
                        'capacity',
                        'feeCondition',
                        'createdDate',
                    ],
                    include: [
                        {
                            model: EventParticipants,
                            attributes: [],
                        },
                    ],
                },
            ],
            attributes: {
                include: [
                    [
                        sequelize.literal(`(
                            SELECT COUNT(*)
                            FROM EventParticipants AS ep
                            WHERE ep.eventID = EventParticipants.eventID
                        )`),
                        'participantCount',
                    ],
                ],
            },
            limit: listItemsPerPage,
            offset: parseInt(page, 10) * listItemsPerPage,
            order: [[sequelize.literal('eventID'), 'DESC']],
        });

        const joinedEventCount = await EventParticipants.count({
            where: {userID: userID},
        });

        res.status(200).json({
            'events': events,
            'total': joinedEventCount,
            'per_page': listItemsPerPage,
        });
    } catch (error) {
        console.error('사용자 이벤트 조회 중 오류:', error);
        res.status(500).json({error: '사용자 이벤트 조회 중 오류가 발생했습니다.'});
    }
}