import { checkAccess } from '../utils/userUtils.js';
import DiaryMemberActivity from '../models/DiaryMemberActivity.js';
import DiaryDateActivity from '../models/DiaryDateActivity.js';
import DiaryDates from '../models/DiaryDates.js';
import Participant from '../models/Participant.js';
import PredefinedActivity from '../models/PredefinedActivity.js';
import GroupActivity from '../models/GroupActivity.js';
import { Op } from 'sequelize';

export const getMemberActivities = async (req, res) => {
    try {
        const { authcode: userToken } = req.headers;
        const { clubId, memberId } = req.params;
        const { period } = req.query; // 'month', '3months', '6months', 'year', 'all'
        
        await checkAccess(userToken, clubId);
        
        // Calculate date range based on period
        const now = new Date();
        let startDate = null;
        
        switch (period) {
            case 'month':
                startDate = new Date(now.getFullYear(), now.getMonth() - 1, now.getDate());
                break;
            case '3months':
                startDate = new Date(now.getFullYear(), now.getMonth() - 3, now.getDate());
                break;
            case '6months':
                startDate = new Date(now.getFullYear(), now.getMonth() - 6, now.getDate());
                break;
            case 'year':
                startDate = new Date(now.getFullYear() - 1, now.getMonth(), now.getDate());
                break;
            case 'all':
            default:
                startDate = null;
                break;
        }
        
        // Get participant ID for this member
        const participants = await Participant.findAll({
            where: { memberId: memberId }
        });
        
        if (participants.length === 0) {
            return res.status(200).json([]);
        }
        
        const participantIds = participants.map(p => p.id);
        
        // Sammle alle Gruppen-IDs, zu denen der Member gehört
        const memberGroupIds = new Set();
        participants.forEach(p => {
            if (p.groupId !== null && p.groupId !== undefined) {
                memberGroupIds.add(p.groupId);
            }
        });
        
        // 1. Get all diary member activities explicitly assigned to this member
        const memberActivities = await DiaryMemberActivity.findAll({
            where: {
                participantId: participantIds
            },
            include: [
                {
                    model: Participant,
                    as: 'participant',
                    attributes: ['id', 'groupId', 'diaryDateId']
                },
                {
                    model: DiaryDateActivity,
                    as: 'activity',
                    include: [
                        {
                            model: DiaryDates,
                            as: 'diaryDate',
                            where: startDate ? {
                                date: {
                                    [Op.gte]: startDate
                                }
                            } : {}
                        },
                        {
                            model: PredefinedActivity,
                            as: 'predefinedActivity'
                        },
                        {
                            model: GroupActivity,
                            as: 'groupActivities',
                            required: false
                        }
                    ]
                }
            ],
            order: [[{ model: DiaryDateActivity, as: 'activity' }, { model: DiaryDates, as: 'diaryDate' }, 'date', 'DESC']]
        });
        
        // 2. Get all group activities for groups the member belongs to
        const groupActivities = [];
        if (memberGroupIds.size > 0) {
            // Suche direkt nach GroupActivity-Einträgen für die Gruppen des Members
            const groupActivitiesData = await GroupActivity.findAll({
                where: {
                    groupId: {
                        [Op.in]: Array.from(memberGroupIds)
                    }
                },
                include: [
                    {
                        model: DiaryDateActivity,
                        as: 'activityGroupActivity',
                        include: [
                            {
                                model: DiaryDates,
                                as: 'diaryDate',
                                where: startDate ? {
                                    date: {
                                        [Op.gte]: startDate
                                    }
                                } : {}
                            },
                            {
                                model: PredefinedActivity,
                                as: 'predefinedActivity',
                                required: false
                            }
                        ]
                    },
                    {
                        model: PredefinedActivity,
                        as: 'groupPredefinedActivity',
                        required: false
                    }
                ]
            });
            
            // Erstelle virtuelle DiaryMemberActivity-Objekte für Gruppen-Aktivitäten
            for (const groupActivity of groupActivitiesData) {
                if (!groupActivity.activityGroupActivity || !groupActivity.activityGroupActivity.diaryDate) {
                    continue; // Überspringe, wenn keine DiaryDateActivity oder kein DiaryDate vorhanden
                }
                
                const activity = groupActivity.activityGroupActivity;
                const diaryDateId = activity.diaryDateId;
                
                // Finde alle relevanten Participants für dieses DiaryDate
                const relevantParticipants = participants.filter(p => 
                    p.diaryDateId === diaryDateId && 
                    p.groupId === groupActivity.groupId
                );
                
                for (const participant of relevantParticipants) {
                    // Verwende die PredefinedActivity aus GroupActivity, falls vorhanden
                    // Sonst die aus DiaryDateActivity
                    const predefinedActivity = groupActivity.groupPredefinedActivity || activity.predefinedActivity;
                    
                    if (predefinedActivity) {
                        // Erstelle ein modifiziertes Activity-Objekt
                        const modifiedActivity = {
                            ...activity.toJSON(),
                            predefinedActivity: predefinedActivity
                        };
                        groupActivities.push({
                            activity: modifiedActivity,
                            participant: participant,
                            id: null // Virtuell, nicht in DB
                        });
                    }
                }
            }
        }
        
        // Filter: explizite Zuordnungen sollen nur dann zählen, wenn
        // - der Participant keine Gruppe hat UND die Aktivität KEINE Gruppenbindung hat, oder
        // - die Aktivität keine Gruppenbindung hat, oder
        // - es eine Gruppenbindung gibt, die zur Gruppe des Participants passt.
        const filteredMemberActivities = memberActivities.filter((ma) => {
            if (!ma?.participant || !ma?.activity) {
                return false;
            }

            const participantGroupId = ma.participant.groupId;
            const groupActivitiesForActivity = ma.activity.groupActivities || [];

            // Participant ohne Gruppe -> nur Aktivitäten ohne Gruppenbindung zählen
            if (participantGroupId === null || participantGroupId === undefined) {
                return !groupActivitiesForActivity.length;
            }

            // Keine Gruppenbindung -> immer zählen
            if (!groupActivitiesForActivity.length) {
                return true;
            }

            // Gruppenbindung vorhanden -> nur zählen, wenn die Gruppe passt
            return groupActivitiesForActivity.some((ga) => Number(ga.groupId) === Number(participantGroupId));
        });

        // 3. Kombiniere beide Listen und entferne Duplikate
        // Ein Duplikat liegt vor, wenn dieselbe Aktivität für denselben Participant bereits explizit zugeordnet ist
        const explicitActivityKeys = new Set();
    filteredMemberActivities.forEach(ma => {
            if (ma.activity && ma.activity.id && ma.participant && ma.participant.id) {
                // Erstelle einen eindeutigen Schlüssel: activityId-participantId
                const key = `${ma.activity.id}-${ma.participant.id}`;
                explicitActivityKeys.add(key);
            }
        });
        
        // Filtere Gruppen-Aktivitäten, die bereits explizit zugeordnet sind
        const uniqueGroupActivities = groupActivities.filter(ga => {
            if (!ga.activity || !ga.activity.id || !ga.participant || !ga.participant.id) {
                return false;
            }
            const key = `${ga.activity.id}-${ga.participant.id}`;
            return !explicitActivityKeys.has(key);
        });
        
        // Kombiniere beide Listen
    const allActivities = [...filteredMemberActivities, ...uniqueGroupActivities];
        
        // Group activities by name and count occurrences
        // Verwende einen Set pro Aktivität, um eindeutige Datum-Aktivität-Kombinationen zu tracken
        const activityMap = new Map();
        
        for (const ma of allActivities) {
            if (!ma.activity || !ma.activity.predefinedActivity || !ma.participant) {
                continue;
            }
            
            const activity = ma.activity.predefinedActivity;
            const activityName = activity.name;
            const activityCode = activity.code || activity.name; // Verwende Code falls vorhanden, sonst Name
            const date = ma.activity.diaryDate?.date;
            
            if (!date) {
                continue; // Überspringe Einträge ohne Datum
            }
            
            // Verwende Code als Key, falls vorhanden, sonst Name
            const key = activityCode;
            
            if (!activityMap.has(key)) {
                activityMap.set(key, {
                    name: activityName, // Vollständiger Name für Tooltip
                    code: activityCode, // Code/Kürzel für Anzeige
                    uniqueDates: new Set(), // Set für eindeutige Daten
                    dates: []
                });
            }
            
            const activityData = activityMap.get(key);
            // Konvertiere Datum zu String für Set-Vergleich (nur Datum, keine Zeit)
            const dateString = date instanceof Date 
                ? date.toISOString().split('T')[0] 
                : new Date(date).toISOString().split('T')[0];
            
            // Füge Datum nur hinzu, wenn es noch nicht vorhanden ist
            if (!activityData.uniqueDates.has(dateString)) {
                activityData.uniqueDates.add(dateString);
                activityData.dates.push(date);
            }
        }
        
        // Konvertiere Sets zu Arrays und setze count basierend auf eindeutigen Daten
        activityMap.forEach((activityData, key) => {
            activityData.count = activityData.uniqueDates.size;
            // Sortiere Daten (neueste zuerst)
            activityData.dates.sort((a, b) => {
                const dateA = new Date(a);
                const dateB = new Date(b);
                return dateB - dateA;
            });
            // Entferne uniqueDates, da es nicht an Frontend gesendet werden muss
            delete activityData.uniqueDates;
        });
        
        // Convert map to array and sort by count
        const activities = Array.from(activityMap.values())
            .sort((a, b) => b.count - a.count);
        
        return res.status(200).json(activities);
        
    } catch (error) {
        console.error('Error fetching member activities:', error);
        return res.status(500).json({ error: 'Failed to fetch member activities' });
    }
};

export const getMemberLastParticipations = async (req, res) => {
    try {
        const { authcode: userToken } = req.headers;
        const { clubId, memberId } = req.params;
        const { limit = 3 } = req.query;
        
        await checkAccess(userToken, clubId);
        
        // Get participant ID for this member
        const participants = await Participant.findAll({
            where: { memberId: memberId }
        });
        
        if (participants.length === 0) {
            return res.status(200).json([]);
        }
        
        const participantIds = participants.map(p => p.id);
        
        // Sammle alle Gruppen-IDs, zu denen der Member gehört
        const memberGroupIds = new Set();
        participants.forEach(p => {
            if (p.groupId !== null && p.groupId !== undefined) {
                memberGroupIds.add(p.groupId);
            }
        });
        
        // 1. Get last participations explicitly assigned to this member
        const memberActivities = await DiaryMemberActivity.findAll({
            where: {
                participantId: participantIds
            },
            include: [
                {
                    model: Participant,
                    as: 'participant',
                    attributes: ['id', 'groupId', 'diaryDateId']
                },
                {
                    model: DiaryDateActivity,
                    as: 'activity',
                    include: [
                        {
                            model: DiaryDates,
                            as: 'diaryDate'
                        },
                        {
                            model: PredefinedActivity,
                            as: 'predefinedActivity'
                        },
                        {
                            model: GroupActivity,
                            as: 'groupActivities',
                            required: false
                        }
                    ]
                }
            ],
            order: [[{ model: DiaryDateActivity, as: 'activity' }, { model: DiaryDates, as: 'diaryDate' }, 'date', 'DESC']],
            limit: parseInt(limit) * 10 // Get more to filter by group
        });

        // Siehe getMemberActivities(): nur zählen, wenn Gruppenbindung passt (oder keine existiert)
        const filteredMemberActivities = memberActivities.filter((ma) => {
            if (!ma?.participant || !ma?.activity) {
                return false;
            }

            const participantGroupId = ma.participant.groupId;
            const groupActivitiesForActivity = ma.activity.groupActivities || [];

            if (!groupActivitiesForActivity.length) {
                return true;
            }

            return groupActivitiesForActivity.some((ga) => Number(ga.groupId) === Number(participantGroupId));
        });
        
        // 2. Get all group activities for groups the member belongs to
        const groupActivities = [];
        if (memberGroupIds.size > 0) {
            // Suche direkt nach GroupActivity-Einträgen für die Gruppen des Members
            const groupActivitiesData = await GroupActivity.findAll({
                where: {
                    groupId: {
                        [Op.in]: Array.from(memberGroupIds)
                    }
                },
                include: [
                    {
                        model: DiaryDateActivity,
                        as: 'activityGroupActivity',
                        include: [
                            {
                                model: DiaryDates,
                                as: 'diaryDate'
                            },
                            {
                                model: PredefinedActivity,
                                as: 'predefinedActivity',
                                required: false
                            }
                        ]
                    },
                    {
                        model: PredefinedActivity,
                        as: 'groupPredefinedActivity',
                        required: false
                    }
                ],
                order: [[{ model: DiaryDateActivity, as: 'activityGroupActivity' }, { model: DiaryDates, as: 'diaryDate' }, 'date', 'DESC']],
                limit: parseInt(limit) * 10 // Get more to filter
            });
            
            // Erstelle virtuelle DiaryMemberActivity-Objekte für Gruppen-Aktivitäten
            for (const groupActivity of groupActivitiesData) {
                if (!groupActivity.activityGroupActivity || !groupActivity.activityGroupActivity.diaryDate) {
                    continue; // Überspringe, wenn keine DiaryDateActivity oder kein DiaryDate vorhanden
                }
                
                const activity = groupActivity.activityGroupActivity;
                const diaryDateId = activity.diaryDateId;
                
                // Finde alle relevanten Participants für dieses DiaryDate
                const relevantParticipants = participants.filter(p => 
                    p.diaryDateId === diaryDateId && 
                    p.groupId === groupActivity.groupId
                );
                
                for (const participant of relevantParticipants) {
                    // Verwende die PredefinedActivity aus GroupActivity, falls vorhanden
                    // Sonst die aus DiaryDateActivity
                    const predefinedActivity = groupActivity.groupPredefinedActivity || activity.predefinedActivity;
                    
                    if (predefinedActivity) {
                        // Erstelle ein modifiziertes Activity-Objekt
                        const modifiedActivity = {
                            ...activity.toJSON(),
                            predefinedActivity: predefinedActivity
                        };
                        groupActivities.push({
                            activity: modifiedActivity,
                            participant: participant,
                            id: null // Virtuell, nicht in DB
                        });
                    }
                }
            }
        }
        
        // 3. Kombiniere beide Listen und entferne Duplikate
        // Ein Duplikat liegt vor, wenn dieselbe Aktivität für denselben Participant bereits explizit zugeordnet ist
        const explicitActivityKeys = new Set();
    filteredMemberActivities.forEach(ma => {
            if (ma.activity && ma.activity.id && ma.participant && ma.participant.id) {
                // Erstelle einen eindeutigen Schlüssel: activityId-participantId
                const key = `${ma.activity.id}-${ma.participant.id}`;
                explicitActivityKeys.add(key);
            }
        });
        
        // Filtere Gruppen-Aktivitäten, die bereits explizit zugeordnet sind
        const uniqueGroupActivities = groupActivities.filter(ga => {
            if (!ga.activity || !ga.activity.id || !ga.participant || !ga.participant.id) {
                return false;
            }
            const key = `${ga.activity.id}-${ga.participant.id}`;
            return !explicitActivityKeys.has(key);
        });
        
        // Kombiniere beide Listen
    const allActivities = [...filteredMemberActivities, ...uniqueGroupActivities];
        
        // Gruppiere nach Datum
        const participationsByDate = new Map();
        
        allActivities
            .filter(ma => {
                if (!ma.activity || !ma.activity.predefinedActivity || !ma.activity.diaryDate || !ma.participant) {
                    return false;
                }
                return true;
            })
            .forEach(ma => {
                const date = ma.activity.diaryDate.date;
                const diaryDateId = ma.activity.diaryDate.id;
                const activity = ma.activity.predefinedActivity;
                const activityName = activity.name;
                const activityCode = activity.code || activity.name;
                
                if (!participationsByDate.has(date)) {
                    participationsByDate.set(date, {
                        date: date,
                        diaryDateId: diaryDateId,
                        activities: []
                    });
                }
                
                const dateEntry = participationsByDate.get(date);
                // Füge Aktivität nur hinzu, wenn sie noch nicht vorhanden ist (vermeide Duplikate)
                // Speichere sowohl code als auch name
                const activityEntry = {
                    code: activityCode,
                    name: activityName
                };
                if (!dateEntry.activities.find(a => (a.code || a.name) === activityCode)) {
                    dateEntry.activities.push(activityEntry);
                }
            });
        
        // Sortiere nach Datum (neueste zuerst) und nehme die letzten N Daten
        const sortedDates = Array.from(participationsByDate.values())
            .sort((a, b) => {
                const dateA = new Date(a.date);
                const dateB = new Date(b.date);
                return dateB - dateA;
            })
            .slice(0, parseInt(limit));
        
        // Formatiere für das Frontend: Flache Liste mit Datum und Aktivität
        const participations = [];
        sortedDates.forEach(dateEntry => {
            dateEntry.activities.forEach(activity => {
                participations.push({
                    id: null, // Virtuell
                    activityName: activity.code || activity.name, // Code für Anzeige
                    activityFullName: activity.name, // Vollständiger Name für Tooltip
                    date: dateEntry.date,
                    diaryDateId: dateEntry.diaryDateId
                });
            });
        });
        
        return res.status(200).json(participations);
        
    } catch (error) {
        console.error('Error fetching member last participations:', error);
        return res.status(500).json({ error: 'Failed to fetch member last participations' });
    }
};

