const database = require('../config/database');
const { Op } = require('sequelize');

/**
 * Service-Klasse für Zeitkorrekturen
 * Enthält die gesamte Business-Logik für Timefixes
 */
class TimefixService {
  constructor() {
    this.defaultUserId = 1;
  }

  /**
   * Holt alle Worklog-Einträge für ein bestimmtes Datum
   * @param {number} userId - Benutzer-ID
   * @param {string} date - Datum im Format YYYY-MM-DD
   * @returns {Promise<Array>} Array von Worklog-Einträgen mit korrigierten Zeiten
   */
  async getWorklogEntriesForDate(userId, date) {
    const { Worklog } = database.getModels();
    const sequelize = database.sequelize;
    
    // Hole alle Worklog-Einträge für das Datum
    const entries = await Worklog.findAll({
      where: {
        user_id: userId,
        tstamp: {
          [Op.like]: `${date}%`
        }
      },
      order: [['tstamp', 'ASC']],
      raw: true
    });
    
    // Hole auch die Timefixes für diese Einträge
    const entryIds = entries.map(e => e.id).filter(Boolean);
    if (entryIds.length === 0) {
      return [];
    }

    const timefixesForEntries = await sequelize.query(
      `SELECT worklog_id, fix_type, fix_date_time FROM timefix WHERE worklog_id IN (:entryIds)`,
      {
        replacements: { entryIds },
        type: sequelize.QueryTypes.SELECT
      }
    );
    
    // Erstelle eine Map: worklog_id -> Timefix
    const timefixMap = new Map();
    timefixesForEntries.forEach(tf => {
      if (!timefixMap.has(tf.worklog_id)) {
        timefixMap.set(tf.worklog_id, []);
      }
      timefixMap.get(tf.worklog_id).push(tf);
    });
    
    // Formatiere die Einträge für das Frontend
    const formattedEntries = entries.map(entry => {
      // Prüfe ob es eine Korrektur gibt
      const timefixForEntry = timefixMap.get(entry.id);
      
      let hours, minutes, action;
      
      if (timefixForEntry && timefixForEntry.length > 0) {
        // Verwende korrigierte Werte aus Timefix
        const tf = timefixForEntry[0];
        
        // WICHTIG: DB speichert UTC, Frontend erwartet lokale Zeit
        if (typeof tf.fix_date_time === 'string') {
          // Parse als UTC und konvertiere zu lokaler Zeit
          const [datePart, timePart] = tf.fix_date_time.split(' ');
          const [h, m, s] = timePart.split(':').map(Number);
          
          // Erstelle UTC-Date aus den Komponenten
          const [year, month, day] = datePart.split('-').map(Number);
          const utcDate = new Date(Date.UTC(year, month - 1, day, h, m, s));
          
          // Extrahiere lokale Stunden/Minuten
          hours = utcDate.getHours();
          minutes = utcDate.getMinutes();
        } else if (tf.fix_date_time instanceof Date) {
          // Date-Objekt: Verwende lokale Zeit
          hours = tf.fix_date_time.getHours();
          minutes = tf.fix_date_time.getMinutes();
        }
        
        action = tf.fix_type;
      } else {
        // Keine Korrektur - verwende Original-Werte aus Worklog
        // WICHTIG: DB speichert UTC, Frontend erwartet lokale Zeit
        if (typeof entry.tstamp === 'string') {
          // Parse als UTC und konvertiere zu lokaler Zeit
          const [datePart, timePart] = entry.tstamp.split(' ');
          const [h, m, s] = timePart.split(':').map(Number);
          
          // Erstelle UTC-Date aus den Komponenten
          const [year, month, day] = datePart.split('-').map(Number);
          const utcDate = new Date(Date.UTC(year, month - 1, day, h, m, s));
          
          // Extrahiere lokale Stunden/Minuten
          hours = utcDate.getHours();
          minutes = utcDate.getMinutes();
        } else if (entry.tstamp instanceof Date) {
          // Date-Objekt: Verwende lokale Zeit
          hours = entry.tstamp.getHours();
          minutes = entry.tstamp.getMinutes();
        }
        
        // Parse state
        action = entry.state;
        if (typeof action === 'string') {
          try {
            const parsed = JSON.parse(action);
            action = parsed.action || action;
          } catch (e) {
            // action bleibt als String
          }
        }
      }
      
      return {
        id: entry.id,
        time: `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`,
        action: action,
        tstamp: entry.tstamp
      };
    }).filter(e => e !== null);
    
    return formattedEntries;
  }

  /**
   * Holt alle Zeitkorrekturen für den heutigen Tag
   * @param {number} userId - Benutzer-ID
   * @returns {Promise<Array>} Array von Zeitkorrekturen
   */
  async getTodayTimefixes(userId) {
    const sequelize = database.sequelize;
    
    // Berechne Start und Ende des heutigen Tages (lokale Zeit)
    const now = new Date();
    const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0);
    const todayEnd = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59);
    
    // Hole alle Timefixes für heute mit Raw SQL
    const todayStartStr = `${todayStart.getFullYear()}-${String(todayStart.getMonth() + 1).padStart(2, '0')}-${String(todayStart.getDate()).padStart(2, '0')} 00:00:00`;
    const todayEndStr = `${todayEnd.getFullYear()}-${String(todayEnd.getMonth() + 1).padStart(2, '0')}-${String(todayEnd.getDate()).padStart(2, '0')} 23:59:59`;
    
    const timefixes = await sequelize.query(
      `SELECT id, user_id, worklog_id, fix_type, fix_date_time 
       FROM timefix 
       WHERE user_id = ? AND fix_date_time BETWEEN ? AND ?
       ORDER BY fix_date_time DESC`,
      {
        replacements: [userId, todayStartStr, todayEndStr],
        type: sequelize.QueryTypes.SELECT
      }
    );
    
    // Hole die zugehörigen Worklog-Einträge separat mit Raw SQL
    const worklogIds = timefixes.map(tf => tf.worklog_id);
    
    if (worklogIds.length === 0) {
      return [];
    }
    
    const worklogs = await sequelize.query(
      `SELECT id, tstamp, state, relatedTo_id FROM worklog WHERE id IN (?)`,
      {
        replacements: [worklogIds],
        type: sequelize.QueryTypes.SELECT
      }
    );
    
    // Erstelle eine Map für schnellen Zugriff
    const worklogMap = new Map();
    worklogs.forEach(wl => {
      worklogMap.set(wl.id, wl);
    });
    
    // Formatiere die Daten für das Frontend
    const formattedTimefixes = timefixes.map(tf => {
      const worklog = worklogMap.get(tf.worklog_id);
      
      if (!worklog || !worklog.tstamp) {
        console.error('Worklog oder tstamp fehlt für Timefix:', tf.id);
        return null;
      }
      
      // KORRIGIERTE Werte aus timefix.fix_date_time
      let newDate, newHours, newMinutes;
      
      if (typeof tf.fix_date_time === 'string') {
        const [datePart, timePart] = tf.fix_date_time.split(' ');
        newDate = datePart;
        [newHours, newMinutes] = timePart.split(':').map(Number);
      } else if (tf.fix_date_time instanceof Date) {
        const d = tf.fix_date_time;
        newDate = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
        newHours = d.getHours();
        newMinutes = d.getMinutes();
      } else {
        console.error('Unbekannter fix_date_time-Typ:', typeof tf.fix_date_time);
        return null;
      }
      
      // ORIGINAL Werte aus worklog
      let originalDate, originalHours, originalMinutes;
      
      if (typeof worklog.tstamp === 'string') {
        const [datePart, timePart] = worklog.tstamp.split(' ');
        originalDate = datePart;
        [originalHours, originalMinutes] = timePart.split(':').map(Number);
      } else if (worklog.tstamp instanceof Date) {
        const d = worklog.tstamp;
        originalDate = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
        originalHours = d.getHours();
        originalMinutes = d.getMinutes();
      } else {
        console.error('Unbekannter tstamp-Typ:', typeof worklog.tstamp);
        return null;
      }
      
      // Parse original action aus worklog.state
      let originalAction = worklog.state;
      if (typeof originalAction === 'string') {
        try {
          const parsed = JSON.parse(originalAction);
          originalAction = parsed.action || originalAction;
        } catch (e) {
          // action bleibt als String
        }
      }
      
      return {
        id: tf.id,
        worklogId: worklog.id,
        fixType: tf.fix_type,
        fixDateTime: tf.fix_date_time,
        // Neue Werte (KORRIGIERT - aus timefix.fix_date_time)
        newDate: newDate,
        newTime: `${newHours.toString().padStart(2, '0')}:${newMinutes.toString().padStart(2, '0')}`,
        newAction: tf.fix_type,
        // Original-Werte (aus worklog)
        originalDate: originalDate,
        originalTime: `${originalHours.toString().padStart(2, '0')}:${originalMinutes.toString().padStart(2, '0')}`,
        originalAction: originalAction
      };
    }).filter(tf => tf !== null);
    
    return formattedTimefixes;
  }

  /**
   * Erstellt eine neue Zeitkorrektur
   * @param {number} userId - Benutzer-ID
   * @param {number} worklogId - Worklog-Eintrag-ID
   * @param {string} newDate - Neues Datum (YYYY-MM-DD)
   * @param {string} newTime - Neue Uhrzeit (HH:MM)
   * @param {string} newAction - Neue Aktion
   * @returns {Promise<Object>} Erstellter Timefix
   */
  async createTimefix(userId, worklogId, newDate, newTime, newAction) {
    const { Timefix } = database.getModels();
    const sequelize = database.sequelize;
    
    // Hole den Original-Worklog-Eintrag mit Raw SQL
    const worklogResult = await sequelize.query(
      `SELECT id, user_id, tstamp, state FROM worklog WHERE id = ?`,
      {
        replacements: [worklogId],
        type: sequelize.QueryTypes.SELECT
      }
    );
    
    if (!worklogResult || worklogResult.length === 0) {
      throw new Error('Worklog-Eintrag nicht gefunden');
    }
    
    const worklog = worklogResult[0];
    
    // Prüfe Berechtigung
    if (worklog.user_id !== userId) {
      throw new Error('Keine Berechtigung für diesen Eintrag');
    }
    
    // Erstelle die korrigierte Zeit als Date-Objekt
    const [newHours, newMinutes] = newTime.split(':').map(Number);
    const [newYear, newMonth, newDay] = newDate.split('-').map(Number);
    const correctedDateTime = new Date(newYear, newMonth - 1, newDay, newHours, newMinutes, 0);
    
    // Prüfe ob bereits ein Timefix existiert
    const existingTimefix = await Timefix.findOne({
      where: {
        worklog_id: worklogId,
        fix_type: newAction
      }
    });
    
    if (existingTimefix) {
      // Update existierender Timefix
      await existingTimefix.update({
        fix_date_time: correctedDateTime
      });
      
      return {
        id: existingTimefix.id,
        worklogId: existingTimefix.worklog_id,
        fixType: existingTimefix.fix_type,
        fixDateTime: existingTimefix.fix_date_time
      };
    }
    
    // Erstelle neuen Timefix
    const timefix = await Timefix.create({
      user_id: userId,
      worklog_id: worklogId,
      fix_type: newAction,
      fix_date_time: correctedDateTime
    });
    
    return {
      id: timefix.id,
      worklogId: timefix.worklog_id,
      fixType: timefix.fix_type,
      fixDateTime: timefix.fix_date_time
    };
  }

  /**
   * Löscht eine Zeitkorrektur
   * @param {number} userId - Benutzer-ID
   * @param {number} timefixId - Timefix-ID
   * @returns {Promise<void>}
   */
  async deleteTimefix(userId, timefixId) {
    const { Timefix } = database.getModels();
    
    const timefix = await Timefix.findByPk(timefixId);
    
    if (!timefix) {
      throw new Error('Zeitkorrektur nicht gefunden');
    }
    
    // Prüfe Berechtigung
    if (timefix.user_id !== userId) {
      throw new Error('Keine Berechtigung für diese Zeitkorrektur');
    }
    
    await timefix.destroy();
  }
}

module.exports = new TimefixService();
