import DiaryDateActivity from '../models/DiaryDateActivity.js';
import GroupActivity from '../models/GroupActivity.js';
import Group from '../models/Group.js';
import PredefinedActivity from '../models/PredefinedActivity.js';
import PredefinedActivityImage from '../models/PredefinedActivityImage.js';
import { checkAccess } from '../utils/userUtils.js';
import { Op } from 'sequelize';

import { devLog } from '../utils/logger.js';
class DiaryDateActivityService {

    async createActivity(userToken, clubId, data) {
        await checkAccess(userToken, clubId);
        const { activity, ...restData } = data;
        const normalizedGroupId = data.groupId === '' || data.groupId === undefined || data.groupId === null
            ? null
            : parseInt(data.groupId, 10);
        // Versuche, die PredefinedActivity robust zu finden:
        // 1) per übergebener ID
        // 2) per Name ODER Code (das Feld "activity" kann Kürzel oder Name sein)
        // 3) erst dann neu anlegen
        let predefinedActivity = null;

        if (data.predefinedActivityId) {
            predefinedActivity = await PredefinedActivity.findByPk(data.predefinedActivityId);
        }

        if (!predefinedActivity) {
            const normalized = (data.activity || '').trim();
            if (normalized) {
                predefinedActivity = await PredefinedActivity.findOne({
                    where: {
                        [Op.or]: [
                            { name: normalized },
                            { code: normalized }
                        ]
                    }
                });
            }
        }

        if (!predefinedActivity) {
            predefinedActivity = await PredefinedActivity.create({
                name: data.name || data.activity || '',
                code: data.code || (data.activity || ''),
                description: data.description || '',
                duration: data.duration && data.duration !== '' ? parseInt(data.duration) : null
            });
        }
        restData.predefinedActivityId = predefinedActivity.id; 
        
        // Bereinige duration-Feld für DiaryDateActivity
        if (restData.duration === '' || restData.duration === undefined) {
            restData.duration = null;
        } else if (typeof restData.duration === 'string') {
            restData.duration = parseInt(restData.duration);
        }
        restData.groupId = Number.isFinite(normalizedGroupId) ? normalizedGroupId : null;

        if (restData.groupId) {
            const group = await Group.findByPk(restData.groupId);
            if (!group || Number(group.diaryDateId) !== Number(data.diaryDateId)) {
                throw new Error('Group isn\'t related to date');
            }
        }
        
        const maxOrderId = await DiaryDateActivity.max('orderId', {
            where: { diaryDateId: data.diaryDateId }
        });
        const newOrderId = maxOrderId !== null ? maxOrderId + 1 : 1;
        restData.orderId = newOrderId;
        return await DiaryDateActivity.create(restData);
    }

    async updateActivity(userToken, clubId, id, data) {
        await checkAccess(userToken, clubId);
        const activity = await DiaryDateActivity.findByPk(id);
        if (!activity) {
            throw new Error('Activity not found');
        }

        // Wenn customActivityName gesendet wird, müssen wir die PredefinedActivity behandeln
        if (data.customActivityName) {
            
            // Suche nach einer existierenden PredefinedActivity mit diesem Namen
            let predefinedActivity = await PredefinedActivity.findOne({ 
                where: { name: data.customActivityName } 
            });
            
            if (!predefinedActivity) {
                // Erstelle eine neue PredefinedActivity
                predefinedActivity = await PredefinedActivity.create({
                    name: data.customActivityName,
                    description: data.description || '',
                    duration: data.duration && data.duration !== '' ? parseInt(data.duration) : (activity.duration || null)
                });
            }
            
            // Setze die predefinedActivityId
            data.predefinedActivityId = predefinedActivity.id;
            
            // Entferne customActivityName aus den zu aktualisierenden Daten
            delete data.customActivityName;
        }

        if (data.groupId !== undefined) {
            const normalizedGroupId = data.groupId === '' || data.groupId === null
                ? null
                : parseInt(data.groupId, 10);
            data.groupId = Number.isFinite(normalizedGroupId) ? normalizedGroupId : null;
            if (data.groupId) {
                const group = await Group.findByPk(data.groupId);
                if (!group || Number(group.diaryDateId) !== Number(activity.diaryDateId)) {
                    throw new Error('Group isn\'t related to date');
                }
            }
        }

        for (const key of Object.keys(data)) {
            if (data[key] === undefined) {
                delete data[key];
            }
        }

        return await activity.update(data);
    }

    async deleteActivity(userToken, clubId, id) {
        await checkAccess(userToken, clubId);
        const activity = await DiaryDateActivity.findByPk(id);
        if (!activity) {
            throw new Error('Activity not found');
        }
        return await activity.destroy();
    }

    async updateActivityOrder(userToken, clubId, id, newOrderId) {
        await checkAccess(userToken, clubId);
        const activity = await DiaryDateActivity.findByPk(id);
        if (!activity) {
            console.error('[DiaryDateActivityService::updateActivityOrder] - Activity not found, throwing error');
            throw new Error('Activity not found');
        }
        const currentOrderId = activity.orderId;
        if (newOrderId < currentOrderId) {
            await DiaryDateActivity.increment(
                { orderId: 1 },
                {
                    where: {
                        diaryDateId: activity.diaryDateId,
                        orderId: { [Op.gte]: newOrderId, [Op.lt]: currentOrderId },
                    },
                }
            );
        } else if (newOrderId > currentOrderId) {
            await DiaryDateActivity.decrement(
                { orderId: 1 },
                {
                    where: {
                        diaryDateId: activity.diaryDateId,
                        orderId: { [Op.lte]: newOrderId, [Op.gt]: currentOrderId },
                    },
                }
            );
        } else {
        }
        activity.orderId = newOrderId;
        const savedActivity = await activity.save();
        return savedActivity;
    }

    async getActivities(userToken, clubId, diaryDateId) {
        await checkAccess(userToken, clubId);
        // Avoid Sequelize eager-loading includes here to prevent EagerLoadingErrors
        // if associations are not initialized in certain environments. Instead,
        // load the core DiaryDateActivity rows and fetch related models separately.
        const activities = await DiaryDateActivity.findAll({
            where: { diaryDateId },
            order: [['orderId', 'ASC']],
        });
        
        // Füge imageUrl zu jeder PredefinedActivity hinzu
        const activitiesWithImages = [];
        for (const activity of activities) {
            const activityData = activity.toJSON();

            // Load predefinedActivity if present
            if (activityData.predefinedActivityId) {
                const predefined = await PredefinedActivity.findByPk(activityData.predefinedActivityId);
                activityData.predefinedActivity = predefined ? predefined.toJSON() : null;

                if (activityData.predefinedActivity) {
                    const allImages = await PredefinedActivityImage.findAll({
                        where: { predefinedActivityId: activityData.predefinedActivity.id },
                        order: [['createdAt', 'ASC']]
                    });
                    const imagesWithParsedData = allImages.map(img => {
                        const imgData = img.toJSON();
                        if (imgData.drawingData) {
                            try { imgData.drawingData = JSON.parse(imgData.drawingData); } catch (err) { console.error(err); }
                        }
                        return imgData;
                    });
                    activityData.predefinedActivity.images = imagesWithParsedData;
                    const firstImage = allImages.length > 0 ? allImages[0] : null;
                    if (activityData.predefinedActivity.drawingData) {
                        try { if (typeof activityData.predefinedActivity.drawingData === 'string') activityData.predefinedActivity.drawingData = JSON.parse(activityData.predefinedActivity.drawingData); } catch (err) { console.error(err); }
                    } else if (firstImage && firstImage.drawingData) {
                        try { activityData.predefinedActivity.drawingData = JSON.parse(firstImage.drawingData); } catch (err) { console.error(err); }
                    }
                    if (firstImage) {
                        activityData.predefinedActivity.imageUrl = `/api/predefined-activities/${activityData.predefinedActivity.id}/image/${firstImage.id}`;
                        activityData.predefinedActivity.imageLink = `/api/predefined-activities/${activityData.predefinedActivity.id}/image/${firstImage.id}`;
                    } else {
                        activityData.predefinedActivity.imageUrl = `/api/predefined-activities/${activityData.predefinedActivity.id}/image`;
                        activityData.predefinedActivity.imageLink = `/api/predefined-activities/${activityData.predefinedActivity.id}/image`;
                    }
                }
            }

            // Load groupActivities separately
            const groupActivities = await GroupActivity.findAll({
                where: { diaryDateActivity: activityData.id },
                order: [['orderId', 'ASC'], ['id', 'ASC']]
            });
            const groupActivitiesData = [];
            for (const ga of groupActivities) {
                const gad = ga.toJSON();
                if (gad.groupId) {
                    const g = await Group.findByPk(gad.groupId);
                    gad.groupsGroupActivity = g ? g.toJSON() : null;
                }
                if (gad.customActivity) {
                    const gp = await PredefinedActivity.findByPk(gad.customActivity);
                    if (gp) {
                        const gpJson = gp.toJSON();
                        const gpImages = await PredefinedActivityImage.findAll({ where: { predefinedActivityId: gpJson.id }, order: [['createdAt','ASC']] });
                        gpJson.images = gpImages.map(i => { const ii = i.toJSON(); try { if (ii.drawingData) ii.drawingData = JSON.parse(ii.drawingData); } catch (e){}; return ii; });
                        const firstImg = gpImages[0];
                        if (gpJson.drawingData) {
                            try { if (typeof gpJson.drawingData === 'string') gpJson.drawingData = JSON.parse(gpJson.drawingData); } catch (e) {}
                        } else if (firstImg && firstImg.drawingData) {
                            try { gpJson.drawingData = JSON.parse(firstImg.drawingData); } catch (e) {}
                        }
                        if (firstImg) {
                            gpJson.imageUrl = `/api/predefined-activities/${gpJson.id}/image/${firstImg.id}`;
                            gpJson.imageLink = `/api/predefined-activities/${gpJson.id}/image/${firstImg.id}`;
                        } else {
                            gpJson.imageUrl = `/api/predefined-activities/${gpJson.id}/image`;
                            gpJson.imageLink = `/api/predefined-activities/${gpJson.id}/image`;
                        }
                        gad.groupPredefinedActivity = gpJson;
                    }
                }
                groupActivitiesData.push(gad);
            }
            activityData.groupActivities = groupActivitiesData;

            activitiesWithImages.push(activityData);
        }
        return activitiesWithImages;
    }

    async addGroupActivity(userToken, clubId, diaryDateId, groupId, activity, predefinedActivityId = null, timeblockId = null, duration = null, durationText = null) {
        await checkAccess(userToken, clubId);
        const group = await Group.findByPk(groupId);
        if (!group) {
            console.error('[DiaryDateActivityService::addGroupActivity] Group not found:', groupId);
            throw new Error('Group not found');
        }

        if (Number(group.diaryDateId) !== Number(diaryDateId)) {
            console.error('[DiaryDateActivityService::addGroupActivity] Group and date don\'t fit');
            console.error('Group diaryDateId:', group.diaryDateId, 'Activity diaryDateId:', diaryDateId);
            throw new Error('Group isn\'t related to date');
        }
        
        let predefinedActivity = null;
        
        // 1. Versuche zuerst, die PredefinedActivity per ID zu finden
        if (predefinedActivityId) {
            predefinedActivity = await PredefinedActivity.findByPk(predefinedActivityId);
        }
        
        // 2. Falls nicht gefunden, suche nach Name oder Code
        if (!predefinedActivity) {
            const normalized = (activity || '').trim();
            if (normalized) {
                predefinedActivity = await PredefinedActivity.findOne({
                    where: {
                        [Op.or]: [
                            { name: normalized },
                            { code: normalized }
                        ]
                    }
                });
            }
        }
        
        // 3. Falls immer noch nicht gefunden, erstelle eine neue
        if (!predefinedActivity) {
            predefinedActivity = await PredefinedActivity.create({
                name: activity || '',
                code: activity || '',
            });
        }
        
        devLog(predefinedActivity);
        const maxOrderId = await DiaryDateActivity.max('orderId', {
            where: { diaryDateId }
        });
        const nextOrderId = Number.isFinite(maxOrderId) ? (maxOrderId + 1) : 1;

        const parsedDuration = duration === '' || duration === undefined || duration === null
            ? null
            : parseInt(duration, 10);
        const normalizedDurationText = durationText === undefined || durationText === null
            ? null
            : String(durationText).trim() || null;

        const activityData = {
            diaryDateId,
            isTimeblock: false,
            predefinedActivityId: predefinedActivity.id,
            groupId: groupId,
            duration: Number.isFinite(parsedDuration) ? parsedDuration : null,
            durationText: normalizedDurationText,
            orderId: nextOrderId
        }
        devLog(activityData);
        return await DiaryDateActivity.create(activityData);
    }

    async updateGroupActivity(userToken, clubId, groupActivityId, predefinedActivityId, duration, durationText, orderId, groupId) {
        await checkAccess(userToken, clubId);
        const planActivity = await DiaryDateActivity.findOne({
            where: { id: groupActivityId, groupId: { [Op.not]: null } }
        });
        if (planActivity) {
            const payload = {};
            if (predefinedActivityId !== undefined && predefinedActivityId !== null) {
                const predefinedActivity = await PredefinedActivity.findByPk(predefinedActivityId);
                if (!predefinedActivity) {
                    throw new Error('Predefined activity not found');
                }
                payload.predefinedActivityId = predefinedActivityId;
            }
            if (groupId !== undefined && groupId !== null) {
                const parsedGroupId = parseInt(groupId, 10);
                if (Number.isFinite(parsedGroupId) && parsedGroupId > 0) {
                    const group = await Group.findByPk(parsedGroupId);
                    if (!group || Number(group.diaryDateId) !== Number(planActivity.diaryDateId)) {
                        throw new Error('Group isn\'t related to date');
                    }
                    payload.groupId = parsedGroupId;
                }
            }
            if (duration !== undefined) {
                const parsedDuration = duration === '' || duration === null
                    ? null
                    : parseInt(duration, 10);
                payload.duration = Number.isFinite(parsedDuration) ? parsedDuration : null;
            }
            if (durationText !== undefined) {
                payload.durationText = durationText === null ? null : (String(durationText).trim() || null);
            }
            if (orderId !== undefined) {
                const parsedOrderId = parseInt(orderId, 10);
                if (Number.isFinite(parsedOrderId) && parsedOrderId > 0) {
                    payload.orderId = parsedOrderId;
                }
            }
            return await planActivity.update(payload);
        }

        const groupActivity = await GroupActivity.findByPk(groupActivityId);
        if (!groupActivity) {
            throw new Error('Group activity not found');
        }
        
        if (predefinedActivityId !== undefined && predefinedActivityId !== null) {
            // Prüfe, ob die PredefinedActivity existiert
            const predefinedActivity = await PredefinedActivity.findByPk(predefinedActivityId);
            if (!predefinedActivity) {
                throw new Error('Predefined activity not found');
            }
            // Aktualisiere die customActivity (die auf die PredefinedActivity verweist)
            groupActivity.customActivity = predefinedActivityId;
        }
        if (groupId !== undefined && groupId !== null) {
            const parsedGroupId = parseInt(groupId, 10);
            if (Number.isFinite(parsedGroupId) && parsedGroupId > 0) {
                groupActivity.groupId = parsedGroupId;
            }
        }
        if (duration !== undefined) {
            const parsedDuration = duration === '' || duration === null
                ? null
                : parseInt(duration, 10);
            groupActivity.duration = Number.isFinite(parsedDuration) ? parsedDuration : null;
        }
        if (durationText !== undefined) {
            groupActivity.durationText = durationText === null ? null : (String(durationText).trim() || null);
        }
        if (orderId !== undefined) {
            const parsedOrderId = parseInt(orderId, 10);
            if (Number.isFinite(parsedOrderId) && parsedOrderId > 0) {
                groupActivity.orderId = parsedOrderId;
            }
        }
        return await groupActivity.save();
    }

    async deleteGroupActivity(userToken, clubId, groupActivityId) {
        await checkAccess(userToken, clubId);
        const planActivity = await DiaryDateActivity.findOne({
            where: { id: groupActivityId, groupId: { [Op.not]: null } }
        });
        if (planActivity) {
            return await planActivity.destroy();
        }
        const groupActivity = await GroupActivity.findByPk(groupActivityId);
        if (!groupActivity) {
            throw new Error('Group activity not found');
        }
        return await groupActivity.destroy();
    }
}

export default new DiaryDateActivityService();
