import BaseService from './BaseService.js';
import UserParamType from '../models/type/user_param.js';
import SettingsType from '../models/type/settings.js';
import UserParam from '../models/community/user_param.js';
import User from '../models/community/user.js';
import UserParamValue from '../models/type/user_param_value.js';
import Interest from '../models/type/interest.js';
import UserInterest from '../models/community/interest.js';
import InterestTranslation from '../models/type/interest_translation.js';
import { Op } from 'sequelize';
import UserParamVisibilityType from '../models/type/user_param_visibility.js';
import UserParamVisibility from '../models/community/user_param_visibility.js';
import { generateIv } from '../utils/encryption.js';

class SettingsService extends BaseService{
    async getUserParams(userId, paramDescriptions) {
        return await UserParam.findAll({
            where: { userId },
            include: [
                {
                    model: UserParamType,
                    as: 'paramType',
                    where: { description: { [Op.in]: paramDescriptions } },
                    order: [[ 'order_id', 'asc' ]]
                }
            ]
        });
    }

    async getFieldOptions(field) {
        if (['singleselect', 'multiselect'].includes(field.datatype)) {
            return await UserParamValue.findAll({
                where: { userParamTypeId: field.id },
                order: [[ 'order_id', 'asc' ]]
            });
        }
        return [];
    } 

    async filterSettings(hashedUserId, type) {
        const user = await this.getUserByHashedId(hashedUserId);
        const userParams = await this.getUserParams(user.id, ['birthdate', 'gender']);
        let birthdate = null;
        let gender = null;
        for (const param of userParams) {
            if (param.paramType.description === 'birthdate') {
                birthdate = param.value;
            }
            if (param.paramType.description === 'gender') {
                const genderResult = await UserParamValue.findOne({ where: { id: param.value } });
                gender = genderResult ? genderResult.dataValues?.value : null;
            }
        }
        const age = birthdate ? this.calculateAge(birthdate) : null;
        const fields = await UserParamType.findAll({
            include: [
                {
                    model: SettingsType,
                    as: 'settings_type',
                    where: { name: type }
                },
                {
                    model: UserParam,
                    as: 'user_params',
                    required: false,
                    include: [
                        {
                            model: User,
                            as: 'user',
                            where: { id: user.id }
                        },
                        {
                            model: UserParamVisibility,
                            as: 'param_visibilities',
                            required: false,
                            include: [
                                {
                                    model: UserParamVisibilityType,
                                    as: 'visibility_type'
                                }
                            ]
                        }
                    ]
                }
            ],
            where: {
                [Op.and]: [
                    { minAge: { [Op.or]: [null, { [Op.lte]: age }] } },
                    { gender: { [Op.or]: [null, gender] } }
                ]
            }
        });
        return await Promise.all(fields.map(async (field) => {
            const options = await this.getFieldOptions(field);
            const visibilityData = field.user_params[0]?.param_visibilities?.[0];
            const visibility = visibilityData
                ? { id: visibilityData.visibility_type?.id, description: visibilityData.visibility_type?.description }
                : { id: null, description: 'Invisible' };
            return {
                id: field.id,
                name: field.description,
                minAge: field.minAge,
                gender: field.gender,
                datatype: field.datatype,
                unit: field.unit,
                immutable: field.immutable,
                value: field.user_params.length > 0 ? field.user_params[0].value : null,
                options: options.map(opt => ({ id: opt.id, value: opt.value })),
                visibility
            };
        }));
    }

    async updateSetting(hashedUserId, settingId, value) {
        try {
            const user = await this.getUserByHashedId(hashedUserId);
            const paramType = await UserParamType.findOne({ where: { id: settingId } });
            if (!paramType) {
                throw new Error('Parameter type not found');
            }
            
            // Prüfe ob das Feld unveränderlich ist
            if (paramType.immutable) {
                const userParam = await UserParam.findOne({
                    where: { userId: user.id, paramTypeId: settingId }
                });
                
                // Wenn bereits ein Wert existiert, ist das Feld unveränderlich
                if (userParam && userParam.value) {
                    throw new Error('This field cannot be changed. Please contact support for modifications.');
                }
            }
            
            const userParam = await UserParam.findOne({
                where: { userId: user.id, paramTypeId: settingId }
            });
            if (userParam) {
                console.log('update param with ', value)
                if (typeof value === 'boolean') {
                    value = value ? 'true' : 'false';
                }
                await userParam.update({value: value});
            } else {
                await UserParam.create(
                    {
                        userId: user.id,
                        paramTypeId: settingId,
                        value: value 
                    }
                );
            }
        } catch (error) {
            console.error('Error updating setting:', hashedUserId, settingId, value, error);
            throw error;
        }
    }

    async getTypeParamValueId(paramValue) {
        const userParamValueObject = await UserParamValue.findOne({
            where: { value: paramValue }
        });
        if (!userParamValueObject) {
            throw new Error('Parameter value not found');
        }
        return userParamValueObject.id;
    }

    async getTypeParamValues(type) {
        const userParamValues = await UserParamValue.findAll({
            include: [
                {
                    model: UserParamType,
                    as: 'user_param_value_type',
                    where: { description: type }
                }
            ]
        });
        return userParamValues.map(type => ({ id: type.dataValues.id, name: type.dataValues.value }));
    }

    async getTypeParamValue(id) {
        const userParamValueObject = await UserParamValue.findOne({
            where: { id }
        });
        if (!userParamValueObject) {
            throw new Error('Parameter value not found');
        }
        return userParamValueObject.value;
    }

    async addInterest(hashedUserId, name) {
        try {
            const user = await this.getUserByHashedId(hashedUserId);

            const existingInterests = await Interest.findAll({ where: { name: name.toLowerCase() } });
            if (existingInterests.length > 0) {
                throw new Error('Interest already exists');
            }

            const userParam = await this.getUserParams(user.id, ['language']);
            let language = 'en';
            if (userParam.length > 0) {
                const userParamValue = await UserParamValue.findOne({
                    where: { id: userParam[0].value }
                });
                language = userParamValue ? userParamValue.value : 'en';
            }

            const languageParam = await UserParamValue.findOne({ where: { value: language } });
            const languageId = languageParam.id;
            const interest = await Interest.create({ name: name.toLowerCase(), allowed: false, adultOnly: true });
            await InterestTranslation.create({ interestsId: interest.id, language: languageId, translation: name });
            return interest;
        } catch (error) {
            console.error('Error adding interest:', error);
            throw error;
        }
    }

    async addUserInterest(hashedUserId, interestId) {
        try {
            const user = await this.getUserByHashedId(hashedUserId);

            const userParams = await this.getUserParams(user.id, ['birthdate']);
            let birthdate = null;
            for (const param of userParams) {
                if (param.paramType.description === 'birthdate') {
                    birthdate = param.value;
                }
            }

            const age = birthdate ? this.calculateAge(birthdate) : 0;
            const interestsFilter = { id: interestId, allowed: true };
            if (age < 18) {
                interestsFilter[Op.or] = [
                    { adultOnly: false },
                    { adultOnly: { [Op.eq]: null } }
                ];
            }

            const existingInterests = await Interest.findAll({ where: interestsFilter });
            if (existingInterests.length === 0) {
                throw new Error('Interest not found');
            };

            const interest = await UserInterest.findAll({
                where: { userId: user.id, userinterestId: interestId }
            });
            if (interest.length > 0) {
                throw new Error('Interest already exists');
            }
            await UserInterest.create({ userId: user.id, userinterestId: interestId });
        } catch (error) {
            console.error('Error adding user interest:', error);
            throw error;
        }
    }

    async removeInterest(hashedUserId, interestId) {
        try {
            const user = await this.getUserByHashedId(hashedUserId);
            const interests = await UserInterest.findAll({
                where: { userId: user.id, userinterestId: interestId }
            });
            for (const interest of interests) {
                await interest.destroy();
            }
        } catch (error) {
            console.error('Error removing interest:', error);
            throw error;
        }
    }

    async getAccountSettings(hashedUserId) {
        try {
            const user = await this.getUserByHashedId(hashedUserId);
            if (!user) {
                throw new Error('User not found');
            }
            
            // Die Email wird automatisch durch den Getter entschlüsselt
            // Falls die Entschlüsselung fehlschlägt, verwende null
            let email = null;
            try {
                email = user.email; // Getter entschlüsselt automatisch
            } catch (decryptError) {
                console.warn('Email decryption failed, using null:', decryptError.message);
                email = null;
            }
            
            return {
                username: user.username,
                email: email,
                showinsearch: user.searchable
            };
        } catch (error) {
            console.error('Error getting account settings:', error);
            throw error;
        }
    }

    async setAccountSettings({ userId, settings }) {
        try {
            const user = await this.getUserByHashedId(userId);
            if (!user) {
                throw new Error('User not found');
            }

            // Update username if provided
            if (settings.username !== undefined) {
                await user.update({ username: settings.username });
            }

            // Update email if provided
            if (settings.email !== undefined) {
                await user.update({ email: settings.email });
            }

            // Update searchable flag if provided
            if (settings.showinsearch !== undefined) {
                await user.update({ searchable: settings.showinsearch });
            }

            // Update password if provided and not empty
            if (settings.newpassword && settings.newpassword.trim() !== '') {
                if (!settings.oldpassword || settings.oldpassword.trim() === '') {
                    throw new Error('Old password is required to change password');
                }
                
                // Verify old password
                const bcrypt = await import('bcryptjs');
                const match = await bcrypt.compare(settings.oldpassword, user.password);
                if (!match) {
                    throw new Error('Old password is incorrect');
                }

                // Hash new password
                const hashedPassword = await bcrypt.hash(settings.newpassword, 10);
                await user.update({ password: hashedPassword });
            }

            return { success: true };
        } catch (error) {
            console.error('Error setting account settings:', error);
            throw error;
        }
    }

    async getVisibilities() {
        return UserParamVisibilityType.findAll();
    }

    async updateVisibility(hashedUserId, userParamTypeId, visibilityId) {
        try {
            const user = await this.getUserByHashedId(hashedUserId);
            if (!user) {
                throw new Error('User not found');
            }
            const userParam = await UserParam.findOne({
                where: { paramTypeId: userParamTypeId, userId: user.id }
            });
            if (!userParam) {
                console.error(`UserParam not found for settingId: ${userParamTypeId} and userId: ${user.id}`);
                throw new Error('User parameter not found or does not belong to the user');
            }
            let userParamVisibility = await UserParamVisibility.findOne({
                where: { param_id: userParam.id }
            });
            if (userParamVisibility) {
                userParamVisibility.visibility = visibilityId;
                await userParamVisibility.save();
            } else {
                await UserParamVisibility.create({
                    param_id: userParam.id,
                    visibility: visibilityId
                });
            }
            console.log(`Visibility updated for settingId: ${userParamTypeId} with visibilityId: ${visibilityId}`);
        } catch (error) {
            console.error('Error updating visibility:', error.message);
            throw error;
        }
    }
}

export default new SettingsService();
