import RoomType from '../models/chat/room_type.js';
import ChatRight from '../models/chat/rights.js';
import UserRight from "../models/community/user_right.js";
import UserRightType from "../models/type/user_right.js";
import InterestType from "../models/type/interest.js";
import InterestTranslationType from "../models/type/interest_translation.js";
import User from "../models/community/user.js";
import UserParamValue from "../models/type/user_param_value.js";
import UserParamType from "../models/type/user_param.js";
import ContactMessage from "../models/service/contactmessage.js";
import ContactService from "./ContactService.js";
import { sendAnswerEmail } from './emailService.js';
import { Op, Sequelize } from 'sequelize';
import FalukantUser from "../models/falukant/data/user.js";
import FalukantCharacter from "../models/falukant/data/character.js";
import FalukantPredefineFirstname from "../models/falukant/predefine/firstname.js";
import FalukantPredefineLastname from "../models/falukant/predefine/lastname.js";
import Branch from "../models/falukant/data/branch.js";
import FalukantStock from "../models/falukant/data/stock.js";
import FalukantStockType from "../models/falukant/type/stock.js";
import RegionData from "../models/falukant/data/region.js";
import RegionType from "../models/falukant/type/region.js";
import BranchType from "../models/falukant/type/branch.js";
import RegionDistance from "../models/falukant/data/region_distance.js";
import Room from '../models/chat/room.js';
import UserParam from '../models/community/user_param.js';
import TitleOfNobility from "../models/falukant/type/title_of_nobility.js";
import { sequelize } from '../utils/sequelize.js';
import npcCreationJobService from './npcCreationJobService.js';
import { v4 as uuidv4 } from 'uuid';

class AdminService {
    async hasUserAccess(userId, section) {
        const userRights = await UserRight.findAll({
            include: [{
                model: UserRightType,
                as: 'rightType',
                where: {
                    title: [section, 'mainadmin'],
                }
            },
            {
                model: User,
                as: 'user_with_rights',
                where: {
                    hashedId: userId,
                }
            }
            ]

        });
        return userRights.length > 0;
    }

    async getOpenInterests(userId) {
        if (!this.hasUserAccess(userId, 'interests')) {
            throw new Error('noaccess');
        }
        const openInterests = await InterestType.findAll({
            where: {
                allowed: false
            },
            include: {
                model: InterestTranslationType,
                as: 'interest_translations',
            }
        })
        return openInterests;
    }

    async changeInterest(userId, interestId, active, adultOnly) {
        if (!this.hasUserAccess(userId, 'interests')) {
            throw new Error('noaccess');
        }
        const interest = await InterestType.findOne({
            where: {
                id: interestId
            }
        });
        if (interest) {
            interest.allowed = active;
            interest.adultOnly = adultOnly;
            await interest.save();
        }
    }

    async deleteInterest(userId, interestId) {
        if (!this.hasUserAccess(userId, 'interests')) {
            throw new Error('noaccess');
        }
        const interest = await InterestType.findOne({
            where: {
                id: interestId
            }
        });
        if (interest) {
            await interest.destroy();
        }
    }

    async changeTranslation(userId, interestId, translations) {
        if (!this.hasUserAccess(userId, 'interests')) {
            throw new Error('noaccess');
        }
        const interest = await InterestType.findOne({
            id: interestId
        });
        if (!interest) {
            throw new Error('notexisting');
        }
        for (const languageId of Object.keys(translations)) {
            const languageObject = await UserParamValue.findOne(
                {
                    where: {
                        id: languageId
                    }
                }
            );
            if (!languageObject) {
                throw new Error('wronglanguage');
            }
            const translation = await InterestTranslationType.findOne(
                {
                    where: {
                        interestsId: interestId,
                        language: languageObject.id
                    }
                }
            );
            if (translation) {
                translation.translation = translations[languageId];
                translation.save();
            } else {
                await InterestTranslationType.create({
                    interestsId: interestId,
                    language: languageObject.id,
                    translation: translations[languageId]

                });
            }
        }
    }

    async getOpenContacts(userId) {
        if (!this.hasUserAccess(userId, 'contacts')) {
            throw new Error('noaccess');
        }
        const openContacts = await ContactMessage.findAll({
            where: {
                isFinished: false,
            }
        })
        return openContacts;
    }

    async answerContact(contactId, answer) {
        const contact = await ContactService.getContactById(contactId);
        await ContactService.saveAnswer(contact, answer);
        await sendAnswerEmail(contact.email, answer, contact.language || 'en');
    }

    async getFalukantUser(userId, userName, characterName) {
        if (!(await this.hasUserAccess(userId, 'falukantusers'))) {
            throw new Error('noaccess');
        }
        let users;
        if (userName) {
            users = await User.findAll({
                where: {
                    username: {
                        [Op.like]: '%' + userName + '%'
                    }
                },
                include: [{
                    model: FalukantUser,
                    as: 'falukantData',
                    required: true,
                    include: [{
                        model: FalukantCharacter,
                        as: 'character',
                        required: true,
                        include: [{
                            model: FalukantPredefineFirstname,
                            as: 'definedFirstName',
                            required: true
                        }, {
                            model: FalukantPredefineLastname,
                            as: 'definedLastName',
                            required: true
                        }]
                    }]
                }]
            });
        } else if (characterName) {
            const [firstname, lastname] = characterName.split(' ');
            users = await User.findAll({
                include: [{
                    model: FalukantUser,
                    as: 'falukantData',
                    required: true,
                    include: [{
                        model: FalukantCharacter,
                        as: 'character',
                        required: true,
                        include: [{
                            model: FalukantPredefineFirstname,
                            as: 'definedFirstName',
                            required: true,
                            where: {
                                name: firstname
                            }
                        }, {
                            model: FalukantPredefineLastname,
                            as: 'definedLastName',
                            required: true,
                            where: {
                                name: lastname
                            }
                        }]
                    }]
                }]
            });
        } else {
            throw new Error('no search parameter');
        }
        return users.map(user => {
            return {
                id: user.hashedId,
                username: user.username,
                falukantUser: user.falukantData
            }
        });
    }

    async getFalukantUserById(userId, hashedId) {
        if (!(await this.hasUserAccess(userId, 'falukantusers'))) {
            throw new Error('noaccess');
        }
        const user = await User.findOne({
            where: {
                hashedId: hashedId
            },
            attributes: ['hashedId', 'username'],
            include: [{
                model: FalukantUser,
                as: 'falukantData',
                required: true,
                attributes: ['money', 'certificate', 'id'],
                include: [{
                    model: FalukantCharacter,
                    as: 'character',
                    attributes: ['birthdate', 'health', 'title_of_nobility'],
                    include: [{
                        model: FalukantPredefineFirstname,
                        as: 'definedFirstName',
                    }, {
                        model: FalukantPredefineLastname,
                        as: 'definedLastName',
                    }]
                }]
            }]
        });
        return user;
    }

    async getFalukantUserBranches(userId, falukantUserId) {
        if (!(await this.hasUserAccess(userId, 'falukantusers'))) {
            throw new Error('noaccess');
        }
        
        try {
            // Zuerst die Branches laden
            const branches = await Branch.findAll({
                where: {
                    falukantUserId: falukantUserId
                }
            });
            
            // Dann für jede Branch die zusätzlichen Daten laden
            const branchesWithData = await Promise.all(branches.map(async (branch) => {
                const region = await RegionData.findByPk(branch.regionId);
                const branchType = await BranchType.findByPk(branch.branchTypeId);
                const stocks = await FalukantStock.findAll({
                    where: { branchId: branch.id },
                    include: [{
                        model: FalukantStockType,
                        as: 'stockType',
                        attributes: ['labelTr']
                    }]
                });
                
                return {
                    ...branch.toJSON(),
                    region: region ? { name: region.name } : null,
                    branchType: branchType ? { labelTr: branchType.labelTr } : null,
                    stocks: stocks
                };
            }));
            
            return branchesWithData;
        } catch (error) {
            console.error('Error in getFalukantUserBranches:', error);
            throw error;
        }
    }

    async getFalukantRegions(userId) {
        if (!(await this.hasUserAccess(userId, 'falukantusers'))) {
            throw new Error('noaccess');
        }

        const regions = await RegionData.findAll({
            attributes: ['id', 'name', 'map'],
            include: [
                {
                    model: RegionType,
                    as: 'regionType',
                    where: { labelTr: 'city' },
                    attributes: ['labelTr'],
                },
            ],
            order: [['name', 'ASC']],
        });

        return regions;
    }

    async getTitlesOfNobility(userId) {
        if (!(await this.hasUserAccess(userId, 'falukantusers'))) {
            throw new Error('noaccess');
        }
        const titles = await TitleOfNobility.findAll({
            order: [['id', 'ASC']],
            attributes: ['id', 'labelTr', 'level']
        });
        return titles;
    }

    async updateFalukantRegionMap(userId, regionId, map) {
        if (!(await this.hasUserAccess(userId, 'falukantusers'))) {
            throw new Error('noaccess');
        }

        const region = await RegionData.findByPk(regionId);
        if (!region) {
            throw new Error('regionNotFound');
        }

        region.map = map || {};
        await region.save();

        return region;
    }

    async getRegionDistances(userId) {
        if (!(await this.hasUserAccess(userId, 'falukantusers'))) {
            throw new Error('noaccess');
        }

        const distances = await RegionDistance.findAll();
        return distances;
    }

    async upsertRegionDistance(userId, { sourceRegionId, targetRegionId, transportMode, distance }) {
        if (!(await this.hasUserAccess(userId, 'falukantusers'))) {
            throw new Error('noaccess');
        }

        if (!sourceRegionId || !targetRegionId || !transportMode) {
            throw new Error('missingParameters');
        }

        const src = await RegionData.findByPk(sourceRegionId);
        const tgt = await RegionData.findByPk(targetRegionId);
        if (!src || !tgt) {
            throw new Error('regionNotFound');
        }

        const mode = String(transportMode);
        const dist = Number(distance);
        if (!Number.isFinite(dist) || dist <= 0) {
            throw new Error('invalidDistance');
        }

        const [record] = await RegionDistance.findOrCreate({
            where: {
                sourceRegionId: src.id,
                targetRegionId: tgt.id,
                transportMode: mode,
            },
            defaults: {
                distance: dist,
            },
        });

        if (record.distance !== dist) {
            record.distance = dist;
            await record.save();
        }

        return record;
    }

    async deleteRegionDistance(userId, id) {
        if (!(await this.hasUserAccess(userId, 'falukantusers'))) {
            throw new Error('noaccess');
        }
        const record = await RegionDistance.findByPk(id);
        if (!record) {
            throw new Error('notfound');
        }
        await record.destroy();
        return { success: true };
    }

    async updateFalukantStock(userId, stockId, quantity) {
        if (!(await this.hasUserAccess(userId, 'falukantusers'))) {
            throw new Error('noaccess');
        }
        
        const stock = await FalukantStock.findByPk(stockId);
        if (!stock) {
            throw new Error('Stock not found');
        }
        
        stock.quantity = quantity;
        await stock.save();
        
        return stock;
    }

    async addFalukantStock(userId, branchId, stockTypeId, quantity) {
        if (!(await this.hasUserAccess(userId, 'falukantusers'))) {
            throw new Error('noaccess');
        }
        
        // Prüfe ob Branch existiert
        const branch = await Branch.findByPk(branchId);
        if (!branch) {
            throw new Error('Branch not found');
        }
        
        // Prüfe ob StockType existiert
        const stockType = await FalukantStockType.findByPk(stockTypeId);
        if (!stockType) {
            throw new Error('Stock type not found');
        }
        
        // Prüfe ob bereits ein Stock dieses Typs für diesen Branch existiert
        const existingStock = await FalukantStock.findOne({
            where: {
                branchId: branchId,
                stockTypeId: stockTypeId
            }
        });
        
        if (existingStock) {
            throw new Error('Stock of this type already exists for this branch');
        }
        
        // Erstelle neuen Stock
        const newStock = await FalukantStock.create({
            branchId: branchId,
            stockTypeId: stockTypeId,
            quantity: quantity
        });
        
        // Lade den neuen Stock mit allen Beziehungen
        const stockWithData = await FalukantStock.findByPk(newStock.id, {
            include: [{ model: FalukantStockType, as: 'stockType', attributes: ['labelTr'] }]
        });
        
        return stockWithData;
    }

    async getFalukantStockTypes(userId) {
        if (!(await this.hasUserAccess(userId, 'falukantusers'))) {
            throw new Error('noaccess');
        }
        
        const stockTypes = await FalukantStockType.findAll({
            attributes: ['id', 'labelTr']
        });
        
        return stockTypes;
    }

    async changeFalukantUser(userId, falukantUserId, falukantData) {
        if (!(await this.hasUserAccess(userId, 'falukantusers'))) {
            throw new Error('noaccess');
        }
        const falukantUser = await FalukantUser.findOne({
            where: {
                id: falukantUserId
            }
        });
        if (!falukantUser) {
            throw new Error('notfound');
        }
        const character = await FalukantCharacter.findOne({
            where: {
                userId: falukantUserId
            }
        });
        if (!character) {
            throw new Error('notfound');
        }
        if (Object.keys(falukantData).indexOf('age') >= 0) {
            const birthDate = (new Date()) - (falukantData.age * 24 * 3600000);
            await character.update({
                birthdate: birthDate
            });
        }
        if (Object.keys(falukantData).indexOf('money')  >= 0) {
            await falukantUser.update({
                money: falukantData.money
            });
        }
        if (Object.keys(falukantData).indexOf('title_of_nobility') >= 0) {
            await character.update({
                titleOfNobility: falukantData.title_of_nobility
            });
        }
        await falukantUser.save();
        await character.save();
    }

    // --- User Administration ---
    async searchUsers(requestingHashedUserId, query) {
        if (!(await this.hasUserAccess(requestingHashedUserId, 'useradministration'))) {
            throw new Error('noaccess');
        }
        if (!query || query.trim().length === 0) return [];

        const users = await User.findAll({
            where: {
                [Op.or]: [
                    { username: { [Op.iLike]: `%${query}%` } },
                    // email is encrypted, can't search directly reliably; skip email search
                ]
            },
            attributes: ['id', 'hashedId', 'username', 'active', 'registrationDate']
        });
        return users.map(u => ({ id: u.hashedId, username: u.username, active: u.active, registrationDate: u.registrationDate }));
    }

    async getUserByHashedId(requestingHashedUserId, targetHashedId) {
        if (!(await this.hasUserAccess(requestingHashedUserId, 'useradministration'))) {
            throw new Error('noaccess');
        }
        const user = await User.findOne({
            where: { hashedId: targetHashedId },
            attributes: ['id', 'hashedId', 'username', 'active', 'registrationDate']
        });
        if (!user) throw new Error('notfound');
        return { id: user.hashedId, username: user.username, active: user.active, registrationDate: user.registrationDate };
    }

    async getUsersByHashedIds(requestingHashedUserId, targetHashedIds) {
        if (!(await this.hasUserAccess(requestingHashedUserId, 'useradministration'))) {
            throw new Error('noaccess');
        }
        if (!Array.isArray(targetHashedIds) || targetHashedIds.length === 0) {
            return [];
        }
        const users = await User.findAll({
            where: { hashedId: { [Op.in]: targetHashedIds } },
            attributes: ['id', 'hashedId', 'username', 'active', 'registrationDate']
        });
        // Erstelle ein Map für schnellen Zugriff
        const userMap = {};
        users.forEach(user => {
            userMap[user.hashedId] = {
                id: user.hashedId,
                username: user.username,
                active: user.active,
                registrationDate: user.registrationDate
            };
        });
        return userMap;
    }

    async updateUser(requestingHashedUserId, targetHashedId, data) {
        if (!(await this.hasUserAccess(requestingHashedUserId, 'useradministration'))) {
            throw new Error('noaccess');
        }
        const user = await User.findOne({ where: { hashedId: targetHashedId } });
        if (!user) throw new Error('notfound');

        const updates = {};
        if (typeof data.username === 'string' && data.username.trim().length > 0) {
            updates.username = data.username.trim();
        }
        if (typeof data.active === 'boolean') {
            updates.active = data.active;
        }
        if (Object.keys(updates).length === 0) return { id: user.hashedId, username: user.username, active: user.active };
        await user.update(updates);
        return { id: user.hashedId, username: user.username, active: user.active };
    }

    // --- User Rights Administration ---
    async listUserRightTypes(requestingHashedUserId) {
        if (!(await this.hasUserAccess(requestingHashedUserId, 'rights'))) {
            throw new Error('noaccess');
        }
        const types = await UserRightType.findAll({ attributes: ['id', 'title'] });
        // map to tr keys if needed; keep title as key used elsewhere
        return types.map(t => ({ id: t.id, title: t.title }));
    }

    async listUserRightsForUser(requestingHashedUserId, targetHashedId) {
        if (!(await this.hasUserAccess(requestingHashedUserId, 'rights'))) {
            throw new Error('noaccess');
        }
        const user = await User.findOne({ where: { hashedId: targetHashedId }, attributes: ['id', 'hashedId', 'username'] });
        if (!user) throw new Error('notfound');
        const rights = await UserRight.findAll({
            where: { userId: user.id },
            include: [{ model: UserRightType, as: 'rightType' }]
        });
        return rights.map(r => ({ rightTypeId: r.rightTypeId, title: r.rightType?.title }));
    }

    async addUserRight(requestingHashedUserId, targetHashedId, rightTypeId) {
        if (!(await this.hasUserAccess(requestingHashedUserId, 'rights'))) {
            throw new Error('noaccess');
        }
        const user = await User.findOne({ where: { hashedId: targetHashedId } });
        if (!user) throw new Error('notfound');
        const type = await UserRightType.findByPk(rightTypeId);
        if (!type) throw new Error('wrongtype');
        const existing = await UserRight.findOne({ where: { userId: user.id, rightTypeId } });
        if (existing) return existing; // idempotent
        const created = await UserRight.create({ userId: user.id, rightTypeId });
        return created;
    }

    async removeUserRight(requestingHashedUserId, targetHashedId, rightTypeId) {
        if (!(await this.hasUserAccess(requestingHashedUserId, 'rights'))) {
            throw new Error('noaccess');
        }
        const user = await User.findOne({ where: { hashedId: targetHashedId } });
        if (!user) throw new Error('notfound');
        await UserRight.destroy({ where: { userId: user.id, rightTypeId } });
        return true;
    }

    // --- Chat Room Admin ---
    async getRoomTypes(userId) {
        if (!(await this.hasUserAccess(userId, 'chatrooms'))) {
            throw new Error('noaccess');
        }
        return await RoomType.findAll();
    }

    async getGenderRestrictions(userId) {
        if (!(await this.hasUserAccess(userId, 'chatrooms'))) {
            throw new Error('noaccess');
        }
        // Find the UserParamType for gender restriction (e.g. description = 'gender')
        const genderType = await UserParamType.findOne({ where: { description: 'gender' } });
        if (!genderType) return [];
        return await UserParamValue.findAll({ where: { userParamTypeId: genderType.id } });
    }

    async getUserRights(userId) {
        if (!(await this.hasUserAccess(userId, 'chatrooms'))) {
            throw new Error('noaccess');
        }
        return await ChatRight.findAll();
    }

    async getRooms(userId) {
        if (!(await this.hasUserAccess(userId, 'chatrooms'))) {
            throw new Error('noaccess');
        }
        // Only return necessary fields to the frontend
        return await Room.findAll({
            attributes: [
                'id',
                'title',
                'roomTypeId',
                'isPublic',
                'genderRestrictionId',
                'minAge',
                'maxAge',
                'friendsOfOwnerOnly',
                'requiredUserRightId',
                'password' // only if needed for editing, otherwise remove
            ],
            include: [
                { model: RoomType, as: 'roomType' },
                { 
                    model: UserParamValue, 
                    as: 'genderRestriction',
                    required: false // Wichtig: required: false, damit auch Räume ohne genderRestriction geladen werden
                },
            ]
        });
    }

    async updateRoom(userId, id, data) {
        if (!(await this.hasUserAccess(userId, 'chatrooms'))) {
            throw new Error('noaccess');
        }
        const room = await Room.findByPk(id);
        if (!room) throw new Error('Room not found');
        await room.update(data);
        return room;
    }

    async createRoom(userId, data) {
        if (!(await this.hasUserAccess(userId, 'chatrooms'))) {
            throw new Error('noaccess');
        }
        return await Room.create(data);
    }

    async deleteRoom(userId, id) {
        if (!(await this.hasUserAccess(userId, 'chatrooms'))) {
            throw new Error('noaccess');
        }
        return await Room.destroy({ where: { id } });
    }

    // --- Match3 Admin Methods ---
    async getMatch3Campaigns(userId) {
        if (!(await this.hasUserAccess(userId, 'match3'))) {
            throw new Error('noaccess');
        }
        const Match3Campaign = (await import('../models/match3/campaign.js')).default;
        return await Match3Campaign.findAll({
            include: [{
                model: (await import('../models/match3/level.js')).default,
                as: 'levels',
                include: [{
                    model: (await import('../models/match3/objective.js')).default,
                    as: 'objectives',
                    required: false
                }],
                required: false
            }]
        });
    }

    async getMatch3Campaign(userId, id) {
        if (!(await this.hasUserAccess(userId, 'match3'))) {
            throw new Error('noaccess');
        }
        const Match3Campaign = (await import('../models/match3/campaign.js')).default;
        return await Match3Campaign.findByPk(id, {
            include: [{
                model: (await import('../models/match3/level.js')).default,
                as: 'levels',
                include: [{
                    model: (await import('../models/match3/objective.js')).default,
                    as: 'objectives',
                    required: false
                }],
                required: false
            }]
        });
    }

    async createMatch3Campaign(userId, data) {
        if (!(await this.hasUserAccess(userId, 'match3'))) {
            throw new Error('noaccess');
        }
        const Match3Campaign = (await import('../models/match3/campaign.js')).default;
        return await Match3Campaign.create(data);
    }

    async updateMatch3Campaign(userId, id, data) {
        if (!(await this.hasUserAccess(userId, 'match3'))) {
            throw new Error('noaccess');
        }
        const Match3Campaign = (await import('../models/match3/campaign.js')).default;
        const campaign = await Match3Campaign.findByPk(id);
        if (!campaign) throw new Error('Campaign not found');
        await campaign.update(data);
        return campaign;
    }

    async deleteMatch3Campaign(userId, id) {
        if (!(await this.hasUserAccess(userId, 'match3'))) {
            throw new Error('noaccess');
        }
        const Match3Campaign = (await import('../models/match3/campaign.js')).default;
        return await Match3Campaign.destroy({ where: { id } });
    }

    async getMatch3Levels(userId) {
        if (!(await this.hasUserAccess(userId, 'match3'))) {
            throw new Error('noaccess');
        }
        const Match3Level = (await import('../models/match3/level.js')).default;
        return await Match3Level.findAll({
            include: [
                {
                    model: (await import('../models/match3/campaign.js')).default,
                    as: 'campaign',
                    required: false
                },
                {
                    model: (await import('../models/match3/objective.js')).default,
                    as: 'objectives',
                    required: false
                }
            ],
            order: [['order', 'ASC']]
        });
    }

    async getMatch3Level(userId, id) {
        if (!(await this.hasUserAccess(userId, 'match3'))) {
            throw new Error('noaccess');
        }
        const Match3Level = (await import('../models/match3/level.js')).default;
        return await Match3Level.findByPk(id, {
            include: [
                {
                    model: (await import('../models/match3/campaign.js')).default,
                    as: 'campaign',
                    required: false
                },
                {
                    model: (await import('../models/match3/objective.js')).default,
                    as: 'objectives',
                    required: false
                }
            ]
        });
    }

    async createMatch3Level(userId, data) {
        if (!(await this.hasUserAccess(userId, 'match3'))) {
            throw new Error('noaccess');
        }
        const Match3Level = (await import('../models/match3/level.js')).default;
        
        // Wenn keine campaignId gesetzt ist, setze eine Standard-Campaign-ID
        if (!data.campaignId) {
            // Versuche eine Standard-Campaign zu finden oder erstelle eine
            const Match3Campaign = (await import('../models/match3/campaign.js')).default;
            let defaultCampaign = await Match3Campaign.findOne({ where: { isActive: true } });
            
            if (!defaultCampaign) {
                // Erstelle eine Standard-Campaign falls keine existiert
                defaultCampaign = await Match3Campaign.create({
                    name: 'Standard Campaign',
                    description: 'Standard Campaign für Match3 Levels',
                    isActive: true,
                    order: 1
                });
            }
            
            data.campaignId = defaultCampaign.id;
        }
        
        // Validiere, dass campaignId gesetzt ist
        if (!data.campaignId) {
            throw new Error('CampaignId ist erforderlich');
        }
        
        return await Match3Level.create(data);
    }

    async updateMatch3Level(userId, id, data) {
        if (!(await this.hasUserAccess(userId, 'match3'))) {
            throw new Error('noaccess');
        }
        const Match3Level = (await import('../models/match3/level.js')).default;
        const level = await Match3Level.findByPk(id);
        if (!level) throw new Error('Level not found');
        await level.update(data);
        return level;
    }

    async deleteMatch3Level(userId, id) {
        if (!(await this.hasUserAccess(userId, 'match3'))) {
            throw new Error('noaccess');
        }
        const Match3Level = (await import('../models/match3/level.js')).default;
        return await Match3Level.destroy({ where: { id } });
    }

    // Match3 Objectives
    async getMatch3Objectives(userId) {
        if (!(await this.hasUserAccess(userId, 'match3'))) {
            throw new Error('noaccess');
        }
        const Match3Objective = (await import('../models/match3/objective.js')).default;
        return await Match3Objective.findAll({
            include: [{
                model: (await import('../models/match3/level.js')).default,
                as: 'level',
                required: false
            }],
            order: [['order', 'ASC']]
        });
    }

    async getMatch3Objective(userId, id) {
        if (!(await this.hasUserAccess(userId, 'match3'))) {
            throw new Error('noaccess');
        }
        const Match3Objective = (await import('../models/match3/objective.js')).default;
        return await Match3Objective.findByPk(id, {
            include: [{
                model: (await import('../models/match3/level.js')).default,
                as: 'level',
                required: false
            }]
        });
    }

    async createMatch3Objective(userId, data) {
        if (!(await this.hasUserAccess(userId, 'match3'))) {
            throw new Error('noaccess');
        }
        const Match3Objective = (await import('../models/match3/objective.js')).default;
        
        // Validiere, dass levelId gesetzt ist
        if (!data.levelId) {
            throw new Error('LevelId ist erforderlich');
        }
        
        // Validiere, dass target eine ganze Zahl ist
        if (data.target && !Number.isInteger(Number(data.target))) {
            throw new Error('Target muss eine ganze Zahl sein');
        }
        
        return await Match3Objective.create(data);
    }

    async updateMatch3Objective(userId, id, data) {
        if (!(await this.hasUserAccess(userId, 'match3'))) {
            throw new Error('noaccess');
        }
        const Match3Objective = (await import('../models/match3/objective.js')).default;
        const objective = await Match3Objective.findByPk(id);
        if (!objective) throw new Error('Objective not found');
        
        // Validiere, dass target eine ganze Zahl ist
        if (data.target && !Number.isInteger(Number(data.target))) {
            throw new Error('Target muss eine ganze Zahl sein');
        }
        
        await objective.update(data);
        return objective;
    }

    async deleteMatch3Objective(userId, id) {
        if (!(await this.hasUserAccess(userId, 'match3'))) {
            throw new Error('noaccess');
        }
        const Match3Objective = (await import('../models/match3/objective.js')).default;
        return await Match3Objective.destroy({ where: { id } });
    }

    async getMatch3TileTypes(userId) {
        if (!(await this.hasUserAccess(userId, 'match3'))) {
            throw new Error('noaccess');
        }
        const Match3TileType = (await import('../models/match3/tileType.js')).default;
        return await Match3TileType.findAll({
            order: [['name', 'ASC']]
        });
    }

    async createMatch3TileType(userId, data) {
        if (!(await this.hasUserAccess(userId, 'match3'))) {
            throw new Error('noaccess');
        }
        const Match3TileType = (await import('../models/match3/tileType.js')).default;
        return await Match3TileType.create(data);
    }

    async updateMatch3TileType(userId, id, data) {
        if (!(await this.hasUserAccess(userId, 'match3'))) {
            throw new Error('noaccess');
        }
        const Match3TileType = (await import('../models/match3/tileType.js')).default;
        const tileType = await Match3TileType.findByPk(id);
        if (!tileType) throw new Error('Tile type not found');
        await tileType.update(data);
        return tileType;
    }

    async deleteMatch3TileType(userId, id) {
        if (!(await this.hasUserAccess(userId, 'match3'))) {
            throw new Error('noaccess');
        }
        const Match3TileType = (await import('../models/match3/tileType.js')).default;
        return await Match3TileType.destroy({ where: { id } });
    }

    async getUserStatistics(userId) {
        if (!(await this.hasUserAccess(userId, 'mainadmin'))) {
            throw new Error('noaccess');
        }

        // Gesamtanzahl angemeldeter Benutzer
        const totalUsers = await User.count({
            where: { active: true }
        });

        // Geschlechterverteilung - ohne raw: true um Entschlüsselung zu ermöglichen
        const genderStats = await UserParam.findAll({
            include: [{
                model: UserParamType,
                as: 'paramType',
                where: { description: 'gender' }
            }]
        });

        const genderDistribution = {};
        for (const stat of genderStats) {
            const genderId = stat.value; // Dies ist die ID des Geschlechts
            if (genderId) {
                const genderValue = await UserParamValue.findOne({
                    where: { id: genderId }
                });
                if (genderValue) {
                    const gender = genderValue.value; // z.B. 'male', 'female'
                    genderDistribution[gender] = (genderDistribution[gender] || 0) + 1;
                }
            }
        }

        // Altersverteilung basierend auf Geburtsdatum - ohne raw: true um Entschlüsselung zu ermöglichen
        const birthdateStats = await UserParam.findAll({
            include: [{
                model: UserParamType,
                as: 'paramType',
                where: { description: 'birthdate' }
            }]
        });

        const ageGroups = {
            'unter 12': 0,
            '12-14': 0,
            '14-16': 0,
            '16-18': 0,
            '18-21': 0,
            '21-25': 0,
            '25-30': 0,
            '30-40': 0,
            '40-50': 0,
            '50-60': 0,
            'über 60': 0
        };

        const now = new Date();
        for (const stat of birthdateStats) {
            try {
                const birthdate = new Date(stat.value);
                if (isNaN(birthdate.getTime())) continue;

                const age = now.getFullYear() - birthdate.getFullYear();
                const monthDiff = now.getMonth() - birthdate.getMonth();
                
                let actualAge = age;
                if (monthDiff < 0 || (monthDiff === 0 && now.getDate() < birthdate.getDate())) {
                    actualAge--;
                }

                if (actualAge < 12) {
                    ageGroups['unter 12']++;
                } else if (actualAge >= 12 && actualAge < 14) {
                    ageGroups['12-14']++;
                } else if (actualAge >= 14 && actualAge < 16) {
                    ageGroups['14-16']++;
                } else if (actualAge >= 16 && actualAge < 18) {
                    ageGroups['16-18']++;
                } else if (actualAge >= 18 && actualAge < 21) {
                    ageGroups['18-21']++;
                } else if (actualAge >= 21 && actualAge < 25) {
                    ageGroups['21-25']++;
                } else if (actualAge >= 25 && actualAge < 30) {
                    ageGroups['25-30']++;
                } else if (actualAge >= 30 && actualAge < 40) {
                    ageGroups['30-40']++;
                } else if (actualAge >= 40 && actualAge < 50) {
                    ageGroups['40-50']++;
                } else if (actualAge >= 50 && actualAge < 60) {
                    ageGroups['50-60']++;
                } else {
                    ageGroups['über 60']++;
                }
            } catch (error) {
                console.error('Fehler beim Verarbeiten des Geburtsdatums:', error);
            }
        }

        return {
            totalUsers,
            genderDistribution,
            ageGroups
        };
    }

    async createNPCs(userId, options) {
        if (!(await this.hasUserAccess(userId, 'falukantusers'))) {
            throw new Error('noaccess');
        }

        const {
            regionIds, // Array von Region-IDs oder null für alle Städte
            minAge, // Mindestalter in Jahren
            maxAge, // Maximalalter in Jahren
            minTitleId, // Minimale Title-ID
            maxTitleId, // Maximale Title-ID
            count // Anzahl der zu erstellenden NPCs
        } = options;

        // Berechne zuerst die Gesamtanzahl, um den Job richtig zu initialisieren
        // WICHTIG: Nur Städte (city) verwenden, keine anderen Region-Typen
        let targetRegions = [];
        if (regionIds && regionIds.length > 0) {
            targetRegions = await RegionData.findAll({
                where: {
                    id: { [Op.in]: regionIds }
                },
                include: [{
                    model: RegionType,
                    as: 'regionType',
                    where: { labelTr: 'city' },
                    required: true // INNER JOIN - nur Regionen mit city-Type
                }]
            });
        } else {
            targetRegions = await RegionData.findAll({
                include: [{
                    model: RegionType,
                    as: 'regionType',
                    where: { labelTr: 'city' },
                    required: true // INNER JOIN - nur Regionen mit city-Type
                }]
            });
        }

        // Zusätzliche Sicherheit: Filtere explizit nach city-Type
        targetRegions = targetRegions.filter(region => {
            return region.regionType && region.regionType.labelTr === 'city';
        });

        console.log(`[createNPCs] Found ${targetRegions.length} cities (filtered)`);
        if (targetRegions.length > 0) {
            console.log(`[createNPCs] City names: ${targetRegions.map(r => r.name).join(', ')}`);
        }

        if (targetRegions.length === 0) {
            throw new Error('No cities found');
        }

        const titles = await TitleOfNobility.findAll({
            where: {
                id: {
                    [Op.between]: [minTitleId, maxTitleId]
                }
            },
            order: [['id', 'ASC']]
        });

        if (titles.length === 0) {
            throw new Error('No titles found in specified range');
        }

        const totalNPCs = targetRegions.length * titles.length * count;

        // Erstelle Job-ID
        const jobId = uuidv4();
        npcCreationJobService.createJob(userId, jobId);
        npcCreationJobService.updateProgress(jobId, 0, totalNPCs);
        npcCreationJobService.setStatus(jobId, 'running');

        // Starte asynchronen Prozess
        this._createNPCsAsync(jobId, userId, {
            regionIds,
            minAge,
            maxAge,
            minTitleId,
            maxTitleId,
            count,
            targetRegions,
            titles
        }).catch(error => {
            console.error('Error in _createNPCsAsync:', error);
            const errorMessage = error?.message || error?.toString() || 'Unknown error occurred';
            npcCreationJobService.setError(jobId, errorMessage);
        });

        return { jobId };
    }

    async _createNPCsAsync(jobId, userId, options) {
        try {
            const {
                regionIds,
                minAge,
                maxAge,
                minTitleId,
                maxTitleId,
                count,
                targetRegions,
                titles
            } = options;

            const genders = ['male', 'female'];
            const createdNPCs = [];
            const totalNPCs = targetRegions.length * titles.length * count;
            let currentNPC = 0;

            console.log(`[NPC Creation Job ${jobId}] Starting creation of ${totalNPCs} NPCs`);

            // Erstelle NPCs in einer Transaktion
            // Für jede Stadt-Titel-Kombination wird die angegebene Anzahl erstellt
            await sequelize.transaction(async (t) => {
            for (const region of targetRegions) {
                for (const title of titles) {
                    // Erstelle 'count' NPCs für diese Stadt-Titel-Kombination
                    for (let i = 0; i < count; i++) {
                        // Zufälliges Geschlecht
                        const gender = genders[Math.floor(Math.random() * genders.length)];

                        // Zufälliger Vorname
                        const firstName = await FalukantPredefineFirstname.findAll({ 
                            where: { gender },
                            order: sequelize.fn('RANDOM'),
                            limit: 1,
                            transaction: t
                        });
                        if (firstName.length === 0) {
                            throw new Error(`No first names found for gender: ${gender}`);
                        }
                        const fnObj = firstName[0];

                        // Zufälliger Nachname
                        const lastName = await FalukantPredefineLastname.findAll({
                            order: sequelize.fn('RANDOM'),
                            limit: 1,
                            transaction: t
                        });
                        if (lastName.length === 0) {
                            throw new Error('No last names found');
                        }
                        const lnObj = lastName[0];

                        // Zufälliges Alter (in Jahren, wird in Tage umgerechnet)
                        const randomAge = Math.floor(Math.random() * (maxAge - minAge + 1)) + minAge;
                        const birthdate = new Date();
                        birthdate.setDate(birthdate.getDate() - randomAge); // 5 Tage = 5 Jahre alt

                        // Erstelle den NPC-Charakter (ohne userId = NPC)
                        const npc = await FalukantCharacter.create({
                            userId: null, // Wichtig: null = NPC
                            regionId: region.id,
                            firstName: fnObj.id,
                            lastName: lnObj.id,
                            gender: gender,
                            birthdate: birthdate,
                            titleOfNobility: title.id,
                            health: 100,
                            moodId: 1
                        }, { transaction: t });

                        createdNPCs.push({
                            id: npc.id,
                            firstName: fnObj.name,
                            lastName: lnObj.name,
                            gender: gender,
                            age: randomAge,
                            region: region.name,
                            title: title.labelTr
                        });

                        // Update Progress
                        currentNPC++;
                        npcCreationJobService.updateProgress(jobId, currentNPC, totalNPCs);
                    }
                }
            }
            });

            console.log(`[NPC Creation Job ${jobId}] Completed: ${createdNPCs.length} NPCs created`);

            // Job abschließen
            npcCreationJobService.setResult(jobId, {
                success: true,
                count: createdNPCs.length,
                countPerCombination: count,
                totalCombinations: targetRegions.length * titles.length,
                npcs: createdNPCs
            });
        } catch (error) {
            console.error(`[NPC Creation Job ${jobId}] Error:`, error);
            throw error; // Re-throw für den catch-Block in createNPCs
        }
    }

    getNPCsCreationStatus(userId, jobId) {
        const job = npcCreationJobService.getJob(jobId);
        if (!job) {
            throw new Error('Job not found');
        }
        if (job.userId !== userId) {
            throw new Error('Access denied');
        }
        return job;
    }
}

export default new AdminService();