import Match3Campaign from '../models/match3/campaign.js';
import Match3Level from '../models/match3/level.js';
import Match3Objective from '../models/match3/objective.js';
import Match3UserProgress from '../models/match3/userProgress.js';
import Match3UserLevelProgress from '../models/match3/userLevelProgress.js';
import Match3TileType from '../models/match3/tileType.js';
import Match3LevelTileType from '../models/match3/levelTileType.js';

class Match3Service {
    // Lade alle aktiven Kampagnen
    async getCampaigns() {
        const campaigns = await Match3Campaign.findAll({
            where: { isActive: true },
            include: [
                {
                    model: Match3Level,
                    as: 'levels',
                    where: { isActive: true },
                    required: false,
                    include: [
                        {
                            model: Match3Objective,
                            as: 'objectives',
                            where: { isRequired: true },
                            required: false
                        }
                    ]
                }
            ],
            order: [
                ['id', 'ASC'],
                [{ model: Match3Level, as: 'levels' }, 'order', 'ASC'],
                [{ model: Match3Level, as: 'levels' }, { model: Match3Objective, as: 'objectives' }, 'order', 'ASC']
            ]
        });

        return campaigns;
    }

    // Lade eine spezifische Kampagne
    async getCampaign(campaignId) {
        try {
            // Lade zuerst die Kampagne ohne levelTileTypes
            const campaign = await Match3Campaign.findByPk(campaignId, {
                include: [
                    {
                        model: Match3Level,
                        as: 'levels',
                        where: { isActive: true },
                        required: false,
                        include: [
                            {
                                model: Match3Objective,
                                as: 'objectives',
                                where: { isRequired: true },
                                required: false
                            }
                        ]
                    }
                ],
                order: [
                    [{ model: Match3Level, as: 'levels' }, 'order', 'ASC'],
                    [{ model: Match3Level, as: 'levels' }, { model: Match3Objective, as: 'objectives' }, 'order', 'ASC']
                ]
            });

            if (!campaign) {
                throw { status: 404, message: 'Campaign not found' };
            }

            // Versuche levelTileTypes zu laden, aber mache es optional
            try {
                const levelsWithTileTypes = await Promise.all(
                    campaign.levels.map(async (level) => {
                        try {
                            const levelTileTypes = await Match3LevelTileType.findAll({
                                where: { 
                                    levelId: level.id, 
                                    isActive: true 
                                },
                                include: [
                                    {
                                        model: Match3TileType,
                                        as: 'tileType',
                                        where: { isActive: true },
                                        required: true
                                    }
                                ],
                                order: [['weight', 'DESC']]
                            });
                            
                            return {
                                ...level.toJSON(),
                                levelTileTypes: levelTileTypes
                            };
                        } catch (error) {
                            console.log(`Warnung: Konnte levelTileTypes für Level ${level.id} nicht laden:`, error.message);
                            // Fallback: Verwende die alten tileTypes
                            return {
                                ...level.toJSON(),
                                levelTileTypes: []
                            };
                        }
                    })
                );
                
                campaign.levels = levelsWithTileTypes;
            } catch (error) {
                console.log('Warnung: Konnte levelTileTypes nicht laden, verwende Fallback:', error.message);
                // Fallback: Verwende die alten tileTypes
                campaign.levels = campaign.levels.map(level => ({
                    ...level.toJSON(),
                    levelTileTypes: []
                }));
            }

            return campaign;
        } catch (error) {
            console.error('Fehler beim Laden der Kampagne:', error);
            throw error;
        }
    }

    // Lade ein spezifisches Level
    async getLevel(levelId) {
        const level = await Match3Level.findByPk(levelId, {
            include: [
                {
                    model: Match3Objective,
                    as: 'objectives',
                    where: { isRequired: true },
                    required: false,
                    order: [['order', 'ASC']]
                }
            ]
        });

        if (!level) {
            throw { status: 404, message: 'Level not found' };
        }

        // Ergänze levelTileTypes wie in getCampaign (optional)
        try {
            const levelTileTypes = await Match3LevelTileType.findAll({
                where: { levelId: level.id, isActive: true },
                include: [
                    {
                        model: Match3TileType,
                        as: 'tileType',
                        where: { isActive: true },
                        required: true
                    }
                ],
                order: [['weight', 'DESC']]
            });
            return { ...level.toJSON(), levelTileTypes };
        } catch (e) {
            return { ...level.toJSON(), levelTileTypes: [] };
        }
    }

    // Lade Benutzer-Fortschritt für eine Kampagne
    async getUserProgress(userId, campaignId) {
        let userProgress = await Match3UserProgress.findOne({
            where: { userId, campaignId },
            include: [
                {
                    model: Match3UserLevelProgress,
                    as: 'levelProgress',
                    include: [
                        {
                            model: Match3Level,
                            as: 'level'
                        }
                    ]
                }
            ]
        });

        if (!userProgress) {
            // Erstelle neuen Fortschritt wenn keiner existiert
            userProgress = await Match3UserProgress.create({
                userId,
                campaignId,
                totalScore: 0,
                totalStars: 0,
                levelsCompleted: 0,
                currentLevel: 1,
                isCompleted: false
            });
        }

        // Validiere und korrigiere currentLevel falls nötig
        if (userProgress.currentLevel < 1 || userProgress.currentLevel > 1000) {
            const correctLevel = userProgress.levelsCompleted + 1;
            await userProgress.update({ currentLevel: correctLevel });
        }

        return userProgress;
    }

        // Aktualisiere Level-Fortschritt
    async updateLevelProgress(userId, campaignId, levelId, score, moves, time, stars, securityHash, timestamp) {
      // ANTI-CHEAT-VALIDIERUNG
      if (!this.validateProgressHash(userId, campaignId, levelId, score, moves, time, stars, securityHash, timestamp)) {
        throw { status: 403, message: 'Progress validation failed - possible cheating detected' };
      }

      // Lade oder erstelle Benutzer-Fortschritt
      let userProgress = await Match3UserProgress.findOne({
        where: { userId, campaignId }
      });

        if (!userProgress) {
            userProgress = await Match3UserProgress.create({
                userId,
                campaignId,
                totalScore: 0,
                totalStars: 0,
                levelsCompleted: 0,
                currentLevel: 1,
                isCompleted: false
            });
        }

        // Lade oder erstelle Level-Fortschritt
        let levelProgress = await Match3UserLevelProgress.findOne({
            where: { userProgressId: userProgress.id, levelId }
        });

        if (!levelProgress) {
            levelProgress = await Match3UserLevelProgress.create({
                userProgressId: userProgress.id,
                levelId,
                score: 0,
                moves: 0,
                time: 0,
                stars: 0,
                isCompleted: false,
                attempts: 0,
                bestScore: 0,
                bestMoves: 0,
                bestTime: 0
            });
        }

        // Aktualisiere Level-Fortschritt
        // WICHTIG: Ein Level ist nur abgeschlossen, wenn es mindestens 1 Stern gibt UND der Score > 0 ist
        // Das verhindert, dass unvollständige Level als abgeschlossen markiert werden
        const isCompleted = stars > 0 && score > 0;
        const attempts = levelProgress.attempts + 1;
        
        await levelProgress.update({
            score: Math.max(score, levelProgress.bestScore),
            moves: moves,
            time: time,
            stars: Math.max(stars, levelProgress.stars),
            // WICHTIG: isCompleted wird NUR auf true gesetzt, wenn das Level tatsächlich abgeschlossen ist
            // Der alte Status wird NICHT beibehalten, da das zu falschen Abschlüssen führen kann
            isCompleted: isCompleted,
            attempts: attempts,
            bestScore: Math.max(score, levelProgress.bestScore),
            bestMoves: Math.min(moves, levelProgress.bestMoves || moves),
            bestTime: Math.min(time, levelProgress.bestTime || time),
            completedAt: isCompleted ? new Date() : levelProgress.completedAt
        });

        // Aktualisiere Gesamt-Fortschritt
        // WICHTIG: Nur wenn das Level abgeschlossen ist, werden Score und Stars zum Gesamtfortschritt hinzugefügt
        // Das verhindert, dass unvollständige Level den Gesamtfortschritt beeinflussen
        let totalScore = userProgress.totalScore;
        let totalStars = userProgress.totalStars;
        
        if (isCompleted) {
          totalScore += score;
          totalStars += stars;
        }
        
        // Berechne neue currentLevel
        // WICHTIG: Zähle nur Level, die tatsächlich abgeschlossen sind
        const levelsCompleted = await Match3UserLevelProgress.count({
            where: { 
                userProgressId: userProgress.id,
                isCompleted: true
            }
        });
        
        // Zusätzliche Sicherheit: Stelle sicher, dass levelsCompleted nicht größer als die verfügbaren Level ist
        if (levelsCompleted > 10) { // Angenommen, es gibt maximal 10 Level
          console.warn(`User ${userId} hat ungewöhnlich viele abgeschlossene Level: ${levelsCompleted}`);
        }

        // WICHTIG: currentLevel sollte nur aktualisiert werden, wenn das Level tatsächlich abgeschlossen ist
        // Wenn das Level nicht abgeschlossen ist, behalte den aktuellen currentLevel bei
        let newCurrentLevel = userProgress.currentLevel;
        
        if (isCompleted) {
            // Nur wenn das Level abgeschlossen ist, setze currentLevel auf das nächste Level
            // WICHTIG: currentLevel ist das nächste zu spielende Level (nicht das letzte abgeschlossene)
            newCurrentLevel = Math.max(userProgress.currentLevel, levelsCompleted + 1);
        }

        await userProgress.update({
            totalScore,
            totalStars,
            levelsCompleted,
            currentLevel: newCurrentLevel,
            lastPlayed: new Date()
        });

        return {
            levelProgress,
            userProgress: {
                totalScore,
                totalStars,
                levelsCompleted,
                currentLevel: newCurrentLevel
            }
        };
    }

    // Bereinige falsche Level-Abschlüsse für einen Benutzer
    async cleanupUserProgress(userId, campaignId) {
      try {
        // Lade Benutzer-Fortschritt
        const userProgress = await Match3UserProgress.findOne({
          where: { userId, campaignId }
        });

        if (!userProgress) {
          return { success: false, message: 'User progress not found' };
        }

        // Lade alle Level-Fortschritte
        const levelProgresses = await Match3UserLevelProgress.findAll({
          where: { userProgressId: userProgress.id }
        });

        let cleanedCount = 0;
        let totalScore = 0;
        let totalStars = 0;
        let levelsCompleted = 0;

        // Bereinige jeden Level-Fortschritt
        for (const levelProgress of levelProgresses) {
          // Ein Level ist nur abgeschlossen, wenn es mindestens 1 Stern UND Score > 0 hat
          const shouldBeCompleted = levelProgress.stars > 0 && levelProgress.score > 0;
          
          if (levelProgress.isCompleted !== shouldBeCompleted) {
            await levelProgress.update({
              isCompleted: shouldBeCompleted,
              completedAt: shouldBeCompleted ? levelProgress.completedAt : null
            });
            cleanedCount++;
          }

          // Sammle korrekte Statistiken
          if (shouldBeCompleted) {
            totalScore += levelProgress.score;
            totalStars += levelProgress.stars;
            levelsCompleted++;
          }
        }

        // Aktualisiere Gesamt-Fortschritt
        await userProgress.update({
          totalScore,
          totalStars,
          levelsCompleted,
          currentLevel: levelsCompleted + 1
        });

        return {
          success: true,
          message: `Cleaned ${cleanedCount} level progress entries`,
          cleanedCount,
          totalScore,
          totalStars,
          levelsCompleted,
          currentLevel: levelsCompleted + 1
        };
      } catch (error) {
        console.error('Error cleaning user progress:', error);
        return { success: false, message: error.message };
      }
    }

    // ANTI-CHEAT: Validiere den Progress-Hash
    validateProgressHash(userId, campaignId, levelId, score, moves, time, stars, securityHash, timestamp) {
      try {
        // Prüfe ob der Timestamp nicht zu alt ist (5 Minuten)
        const now = Date.now();
        if (now - timestamp > 5 * 60 * 1000) {
          console.warn(`Progress validation failed: Timestamp too old for user ${userId}`);
          return false;
        }

        // Lade Level-Daten für Hash-Validierung
        return Match3Level.findByPk(levelId).then(level => {
          if (!level) {
            console.warn(`Progress validation failed: Level ${levelId} not found`);
            return false;
          }

          // Erstelle den gleichen Hash wie im Frontend
          const dataString = `${levelId}|${score}|${moves}|${stars}|true|${level.boardLayout}|${level.moveLimit}`;
          
          // Einfache Hash-Funktion (muss mit Frontend übereinstimmen)
          let hash = 0;
          for (let i = 0; i < dataString.length; i++) {
            const char = dataString.charCodeAt(i);
            hash = ((hash << 5) - hash) + char;
            hash = hash & hash;
          }
          
          // Salt hinzufügen (muss mit Frontend übereinstimmen)
          const salt = 'YourPart3_Match3_Security_2024';
          const saltedString = `${dataString}|${salt}`;
          
          let saltedHash = 0;
          for (let i = 0; i < saltedString.length; i++) {
            const char = saltedString.charCodeAt(i);
            saltedHash = ((saltedHash << 5) - hash) + char;
            saltedHash = saltedHash & saltedHash;
          }
          
          const expectedHash = Math.abs(saltedHash).toString(16);
          
          // Vergleiche Hash
          if (expectedHash !== securityHash) {
            console.warn(`Progress validation failed: Hash mismatch for user ${userId}. Expected: ${expectedHash}, Got: ${securityHash}`);
            return false;
          }
          
          // Zusätzliche Validierungen
          if (score < 0 || moves < 0 || stars < 0 || stars > 3) {
            console.warn(`Progress validation failed: Invalid values for user ${userId}`);
            return false;
          }
          
          // Prüfe ob der Score realistisch ist (basierend auf Moves und Level)
          const maxPossibleScore = moves * 100 * levelId; // Vereinfachte Berechnung
          if (score > maxPossibleScore * 2) { // Erlaube 2x den maximalen Score
            console.warn(`Progress validation failed: Unrealistic score for user ${userId}`);
            return false;
          }
          
          return true;
        });
      } catch (error) {
        console.error('Progress validation error:', error);
        return false;
      }
    }

    // Lade Benutzer-Statistiken
    async getUserStats(userId) {
        const userProgress = await Match3UserProgress.findAll({
            where: { userId },
            include: [
                {
                    model: Match3UserLevelProgress,
                    as: 'levelProgress',
                    include: [
                        {
                            model: Match3Level,
                            as: 'level'
                        }
                    ]
                }
            ]
        });

        if (!userProgress || userProgress.length === 0) {
            return {
                totalScore: 0,
                totalStars: 0,
                levelsCompleted: 0,
                totalPlayTime: 0,
                averageScore: 0,
                bestLevel: null
            };
        }

        const totalScore = userProgress.reduce((sum, campaign) => sum + campaign.totalScore, 0);
        const totalStars = userProgress.reduce((sum, campaign) => sum + campaign.totalStars, 0);
        const levelsCompleted = userProgress.reduce((sum, campaign) => sum + campaign.levelsCompleted, 0);
        
        let totalPlayTime = 0;
        let totalLevels = 0;
        let bestLevel = null;
        let bestScore = 0;

        userProgress.forEach(campaign => {
            campaign.levelProgress.forEach(level => {
                if (level.time) {
                    totalPlayTime += level.time;
                    totalLevels++;
                }
                if (level.score > bestScore) {
                    bestScore = level.score;
                    bestLevel = level.level;
                }
            });
        });

        return {
            totalScore,
            totalStars,
            levelsCompleted,
            totalPlayTime,
            averageScore: totalLevels > 0 ? Math.round(totalScore / totalLevels) : 0,
            bestLevel: bestLevel ? {
                name: bestLevel.name,
                score: bestScore
            } : null
        };
    }
};

export default new Match3Service();
