import bcrypt from 'bcryptjs';
import crypto from 'crypto';
import { v4 as uuidv4 } from 'uuid';
import User from '../models/community/user.js';
import UserParam from '../models/community/user_param.js';
import UserParamType from '../models/type/user_param.js';
import { sendAccountActivationEmail, sendPasswordResetEmail } from './emailService.js';
import { sequelize } from '../utils/sequelize.js';
import { Op } from 'sequelize';
import { setUserSession, deleteUserSession } from '../utils/redis.js';
import { encrypt } from '../utils/encryption.js';
import { notifyUser } from '../utils/socket.js';
import Friendship from '../models/community/friendship.js';

const saltRounds = 10;

const getFriends = async (userId) => {
    console.log('getFriends', userId);
    try {
        const friendships = await Friendship.findAll({
            where: {
                [Op.or]: [
                    { user1Id: userId },
                    { user2Id: userId },
                ],
                accepted: true,
            },
            include: [
                {
                    model: User,
                    as: 'friendSender',
                    attributes: ['hashedId', 'username'],
                },
                {
                    model: User,
                    as: 'friendReceiver',
                    attributes: ['hashedId', 'username'],
                },
            ],
        });

        console.log('did read out friends');
        return friendships.map((friendship) => {
            if (friendship.user1Id === userId) {
                return friendship.friendReceiver;
            } else {
                return friendship.friendSender;
            }
        });
    } catch (error) {
        console.error('Error fetching friends:', error);
        throw error;
    }
};

export const registerUser = async ({ email, username, password, language }) => {
    const encryptedEmail = encrypt(email);
    const query = `
        SELECT id FROM community.user
        WHERE email = :encryptedEmail
    `;
    const existingUser = await sequelize.query(query, {
        replacements: { encryptedEmail },
        type: sequelize.QueryTypes.SELECT,
    });
    if (existingUser.length > 0) {
        throw new Error('emailinuse');
    }
    const hashedPassword = await bcrypt.hash(password, saltRounds);
    const resetToken = uuidv4();
    const user = await User.create({
        email: email,
        username,
        password: hashedPassword,
        resetToken: resetToken,
        active: false,
        registration_date: new Date()
    });
    const languageType = await UserParamType.findOne({ where: { description: 'language' } });
    if (!languageType) {
        throw new Error('languagenotfound');
    }
    const languageParam = await UserParam.create({
        userId: user.id,
        paramTypeId: languageType.id,
        value: language
    });
    const activationLink = `${process.env.FRONTEND_URL}/activate?token=${resetToken}`;
    await sendAccountActivationEmail(email, activationLink, username, resetToken, language);
    const authCode = crypto.randomBytes(20).toString('hex');
    return { id: user.hashedId, username: user.username, active: user.active, param: [languageParam], authCode };
};

export const loginUser = async ({ username, password }) => {
    const user = await User.findOne({ where: { username } });
    if (!user) {
        throw new Error('credentialsinvalid');
    }
    const match = await bcrypt.compare(password, user.password);
    if (!match) {
        throw new Error('credentialsinvalid');
    }
    const authCode = crypto.randomBytes(20).toString('hex');
    user.authCode = authCode;
    await user.save();
    const friends = await getFriends(user.id);
    for (const friend of friends) {
        await notifyUser(friend.hashedId, 'friendloginchanged', {
            userId: user.hashedId,
            status: 'online',
        });
    }
    console.log('set user session');
    const sessionData = {
        id: user.hashedId,
        username: user.username,
        active: user.active,
        authCode,
        timestamp: Date.now()
    };
    await setUserSession(user.id, sessionData);
    console.log('get user params');
    const params = await UserParam.findAll({
        where: {
            userId: user.id
        },
        include: {
            model: UserParamType,
            as: 'paramType',
            where: {
                description: ['birthdate', 'gender', 'language']
            }
        }
    });
    const mappedParams = params.map(param => {
        return { 'name': param.paramType.description, 'value': param.value };
    });
    console.log('return user');
    return {
        id: user.hashedId,
        username: user.username,
        active: user.active,
        param: mappedParams,
        authCode
    };
};

export const logoutUser = async (hashedUserId) => {
    try {
        const user = await User.findOne({
            where: {
                hashedId: hashedUserId
            }
        })
        if (!user) {
            return;
        }
        const friends = await getFriends(user.id);
        for (const friend of friends) {
            await notifyUser(friend.hashedId, 'friendloginchanged', {
                userId: user.hashedId,
                status: 'online',
            });
        }
        await deleteUserSession(user.id);
    } catch (error) {
        console.error('Fehler beim Logout:', error);
        throw new Error('logoutfailed');
    }
};

export const handleForgotPassword = async ({ email }) => {
    const user = await User.findOne({ where: { email } });
    if (!user) {
        throw new Error('Email not found');
    }
    const resetToken = uuidv4();
    const resetLink = `${process.env.FRONTEND_URL}/reset-password?token=${resetToken}`;
    await user.update({ reset_token: resetToken });
    const languageParam = await UserParam.findOne({ where: { user_id: user.id, param_type_id: languageType.id } });
    const userLanguage = languageParam ? languageParam.value : 'en';
    await sendPasswordResetEmail(email, resetLink, userLanguage);
    return { message: 'Password reset email sent' };
};

export const activateUserAccount = async ({ token }) => {
    const user = await User.findOne({ where: { reset_token: token } });
    if (!user) {
        throw new Error('Invalid token');
    }
    await user.update({ active: true, reset_token: null });
    return { message: 'Account activated' };
};
