import models from '../models/index.js';
import BaseService from './BaseService.js';

const { TaxiHighscore, User, TaxiMap } = models;

class TaxiHighscoreService extends BaseService {
  constructor() {
    super();
    this.model = TaxiHighscore;
  }

  /**
   * Speichert oder aktualisiert einen Highscore-Eintrag
   * Jeder User kann nur einen Eintrag pro Map haben (der beste wird gespeichert)
   * @param {Object} highscoreData - Die Highscore-Daten
   * @param {string} highscoreData.hashedUserId - Hash-ID des Users
   * @param {string} highscoreData.nickname - Nickname des Users
   * @param {number} highscoreData.passengersDelivered - Anzahl abgelieferter Passagiere
   * @param {number} highscoreData.playtime - Spielzeit in Sekunden
   * @param {number} highscoreData.points - Erreichte Punkte
   * @param {number} highscoreData.mapId - ID der Map
   * @param {string} highscoreData.mapName - Name der Map
   * @returns {Promise<Object>} Gespeicherter oder aktualisierter Highscore-Eintrag
   */
  async createHighscore(highscoreData) {
    try {
      // Hash-ID zu echter User-ID konvertieren
      const user = await this.getUserByHashedId(highscoreData.hashedUserId);
      const userId = user.id;
      
      // Prüfen ob bereits ein Eintrag für diesen User und diese Map existiert
      const existingHighscore = await this.model.findOne({
        where: {
          userId: userId,
          mapId: highscoreData.mapId
        }
      });

      if (existingHighscore) {
        // Nur aktualisieren wenn der neue Score besser ist (mehr Punkte)
        if (highscoreData.points > existingHighscore.points) {
          await existingHighscore.update({
            nickname: highscoreData.nickname,
            passengersDelivered: highscoreData.passengersDelivered,
            playtime: highscoreData.playtime,
            points: highscoreData.points,
            mapName: highscoreData.mapName
          });
          return existingHighscore;
        } else {
          // Neuer Score ist nicht besser, existierenden zurückgeben
          return existingHighscore;
        }
      } else {
        // Kein existierender Eintrag, neuen erstellen
        const highscore = await this.model.create({
          userId: userId,
          nickname: highscoreData.nickname,
          passengersDelivered: highscoreData.passengersDelivered,
          playtime: highscoreData.playtime,
          points: highscoreData.points,
          mapId: highscoreData.mapId,
          mapName: highscoreData.mapName
        });

        return highscore;
      }
    } catch (error) {
      console.error('Fehler beim Erstellen/Aktualisieren des Highscores:', error);
      throw error;
    }
  }

  /**
   * Holt die Top-Highscores für eine bestimmte Map
   * @param {number} mapId - ID der Map (optional)
   * @param {number} limit - Anzahl der Einträge (Standard: 10)
   * @param {string} orderBy - Sortierung ('points', 'passengersDelivered', 'playtime')
   * @returns {Promise<Array>} Array der Highscore-Einträge
   */
  async getTopHighscores(mapId = null, limit = 10, orderBy = 'points') {
    try {
      const whereClause = mapId ? { mapId } : {};
      
      const highscores = await this.model.findAll({
        where: whereClause,
        include: [
          {
            model: User,
            as: 'user',
            attributes: ['id', 'username'],
            required: false // LEFT JOIN, da User gelöscht sein könnte
          },
          {
            model: TaxiMap,
            as: 'map',
            attributes: ['id', 'name'],
            required: false
          }
        ],
        order: [[orderBy, 'DESC']],
        limit: parseInt(limit)
      });

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

  /**
   * Holt die persönlichen Bestleistungen eines Users
   * @param {string} hashedUserId - Hash-ID des Users
   * @param {number} mapId - ID der Map (optional)
   * @returns {Promise<Object>} Bestleistungen des Users
   */
  async getUserBestScores(hashedUserId, mapId = null) {
    try {
      // Hash-ID zu echter User-ID konvertieren
      const user = await this.getUserByHashedId(hashedUserId);
      const userId = user.id;
      
      const whereClause = { userId };
      if (mapId) {
        whereClause.mapId = mapId;
      }

      const [bestPoints, bestPassengers, bestPlaytime] = await Promise.all([
        this.model.findOne({
          where: whereClause,
          order: [['points', 'DESC']]
        }),
        this.model.findOne({
          where: whereClause,
          order: [['passengersDelivered', 'DESC']]
        }),
        this.model.findOne({
          where: whereClause,
          order: [['playtime', 'DESC']]
        })
      ]);

      return {
        bestPoints,
        bestPassengers,
        bestPlaytime
      };
    } catch (error) {
      console.error('Fehler beim Laden der User-Bestleistungen:', error);
      throw error;
    }
  }

  /**
   * Holt alle Highscores eines Users
   * @param {string} hashedUserId - Hash-ID des Users
   * @param {number} limit - Anzahl der Einträge (Standard: 20)
   * @returns {Promise<Array>} Array der Highscore-Einträge des Users
   */
  async getUserHighscores(hashedUserId, limit = 20) {
    try {
      // Hash-ID zu echter User-ID konvertieren
      const user = await this.getUserByHashedId(hashedUserId);
      const userId = user.id;
      
      const highscores = await this.model.findAll({
        where: { userId },
        include: [
          {
            model: TaxiMap,
            as: 'map',
            attributes: ['id', 'name']
          }
        ],
        order: [['createdAt', 'DESC']],
        limit: parseInt(limit)
      });

      return highscores;
    } catch (error) {
      console.error('Fehler beim Laden der User-Highscores:', error);
      throw error;
    }
  }

  /**
   * Holt die Rangliste-Position eines Users für eine bestimmte Map
   * @param {string} hashedUserId - Hash-ID des Users
   * @param {number} mapId - ID der Map (optional)
   * @param {string} orderBy - Sortierung ('points', 'passengersDelivered', 'playtime')
   * @returns {Promise<number>} Rangliste-Position (1-basiert)
   */
  async getUserRank(hashedUserId, mapId = null, orderBy = 'points') {
    try {
      // Hash-ID zu echter User-ID konvertieren
      const user = await this.getUserByHashedId(hashedUserId);
      const userId = user.id;
      
      const whereClause = mapId ? { mapId } : {};
      
      const userHighscore = await this.model.findOne({
        where: { userId, ...whereClause },
        order: [[orderBy, 'DESC']]
      });

      if (!userHighscore) {
        return null; // User hat noch keinen Highscore
      }

      const rank = await this.model.count({
        where: {
          ...whereClause,
          [orderBy]: {
            [this.model.sequelize.Sequelize.Op.gt]: userHighscore[orderBy]
          }
        }
      });

      return rank + 1; // 1-basierte Position
    } catch (error) {
      console.error('Fehler beim Berechnen der User-Rangliste:', error);
      throw error;
    }
  }

  /**
   * Löscht alle Highscores eines Users (z.B. bei Account-Löschung)
   * @param {number} userId - ID des Users
   * @returns {Promise<number>} Anzahl der gelöschten Einträge
   */
  async deleteUserHighscores(userId) {
    try {
      const deletedCount = await this.model.destroy({
        where: { userId }
      });

      return deletedCount;
    } catch (error) {
      console.error('Fehler beim Löschen der User-Highscores:', error);
      throw error;
    }
  }

  /**
   * Holt Statistiken über die Highscores
   * @returns {Promise<Object>} Statistiken
   */
  async getHighscoreStats() {
    try {
      const [
        totalHighscores,
        totalPlayers,
        averagePoints,
        totalPassengersDelivered,
        totalPlaytime
      ] = await Promise.all([
        this.model.count(),
        this.model.count({
          distinct: true,
          col: 'userId'
        }),
        this.model.findOne({
          attributes: [
            [this.model.sequelize.fn('AVG', this.model.sequelize.col('points')), 'avg']
          ],
          raw: true
        }),
        this.model.sum('passengersDelivered'),
        this.model.sum('playtime')
      ]);

      return {
        totalHighscores,
        totalPlayers,
        averagePoints: averagePoints ? parseFloat(averagePoints.avg).toFixed(2) : 0,
        totalPassengersDelivered: totalPassengersDelivered || 0,
        totalPlaytime: totalPlaytime || 0
      };
    } catch (error) {
      console.error('Fehler beim Laden der Highscore-Statistiken:', error);
      throw error;
    }
  }
}

export default new TaxiHighscoreService();
