import MyTischtennis from '../models/MyTischtennis.js';
import MyTischtennisUpdateHistory from '../models/MyTischtennisUpdateHistory.js';
import User from '../models/User.js';
import myTischtennisClient from '../clients/myTischtennisClient.js';
import HttpError from '../exceptions/HttpError.js';

import { devLog } from '../utils/logger.js';
class MyTischtennisService {
  /**
   * Get myTischtennis account for user
   */
  async getAccount(userId) {
    const account = await MyTischtennis.findOne({
      where: { userId }
      // Keine attributes-Limitierung, damit getter-Methoden für Verschlüsselung funktionieren
    });
    if (!account) {
      return null;
    }
    // Rückgabe mit automatischer Entschlüsselung durch Model-Getters
    return {
      id: account.id,
      userId: account.userId,
      email: account.email, // Automatisch entschlüsselt
      savePassword: account.savePassword,
      autoUpdateRatings: account.autoUpdateRatings,
      lastLoginAttempt: account.lastLoginAttempt,
      lastLoginSuccess: account.lastLoginSuccess,
      lastUpdateRatings: account.lastUpdateRatings,
      expiresAt: account.expiresAt,
      userData: account.userData, // Automatisch entschlüsselt
      clubId: account.clubId, // Automatisch entschlüsselt
      clubName: account.clubName, // Automatisch entschlüsselt
      fedNickname: account.fedNickname, // Automatisch entschlüsselt
      createdAt: account.createdAt,
      updatedAt: account.updatedAt
    };
  }

  /**
   * Create or update myTischtennis account
   */
  async upsertAccount(userId, email, password, savePassword, autoUpdateRatings, userPassword) {
    // Verify user's app password
    const user = await User.findByPk(userId);
    if (!user) {
      throw new HttpError({ code: 'ERROR_USER_NOT_FOUND' }, 404);
    }

    let loginResult = null;
    
    // Wenn ein Passwort gesetzt/geändert wird, App-Passwort verifizieren
    if (password) {
      const isValidPassword = await user.validatePassword(userPassword);
      if (!isValidPassword) {
        throw new HttpError('Ungültiges Passwort', 401);
      }

      // Login-Versuch bei myTischtennis
      loginResult = await myTischtennisClient.login(email, password);
      if (!loginResult.success && loginResult.requiresCaptcha) {
        console.log('[myTischtennisService.upsertAccount] CAPTCHA-Fehler, versuche Playwright-Fallback...');
        // Load saved browser state so we can skip the CAPTCHA if the previous session is still valid.
        const existingAccount = await MyTischtennis.findOne({ where: { userId } });
        const savedStorageState = existingAccount?.playwrightStorageState ?? null;
        const playwrightResult = await myTischtennisClient.loginWithBrowserAutomation(
          email, password, { savedStorageState }
        );
        if (playwrightResult.success) {
          loginResult = playwrightResult;
        } else {
          console.warn('[myTischtennisService.upsertAccount] Playwright-Fallback fehlgeschlagen:', playwrightResult.error);
        }
      }
      if (!loginResult.success) {
        const statusCode = loginResult.requiresCaptcha ? 400 : 401;
        const errorMessage = loginResult.error || 'myTischtennis-Login fehlgeschlagen. Bitte überprüfen Sie Ihre Zugangsdaten.';
        if (loginResult.requiresCaptcha) {
          throw new HttpError({ code: 'ERROR_MYTISCHTENNIS_CAPTCHA_REQUIRED', params: { message: errorMessage } }, statusCode);
        }
        throw new HttpError(errorMessage, statusCode);
      }
    }

    // Find or create account
    let account = await MyTischtennis.findOne({ where: { userId } });
    
    const now = new Date();
    
    if (account) {
      const effectiveAutoUpdateRatings = autoUpdateRatings === undefined
        ? account.autoUpdateRatings
        : Boolean(autoUpdateRatings);

      // Update existing
      account.email = email;
      account.savePassword = savePassword;
      account.autoUpdateRatings = savePassword ? effectiveAutoUpdateRatings : false;
      
      if (password && savePassword) {
        account.setPassword(password);
      } else if (!savePassword) {
        account.encryptedPassword = null;
      }
      
      if (loginResult && loginResult.success) {
        account.lastLoginAttempt = now;
        account.lastLoginSuccess = now;
        account.accessToken = loginResult.accessToken;
        account.refreshToken = loginResult.refreshToken;
        account.expiresAt = loginResult.expiresAt;
        account.cookie = loginResult.cookie;
        account.userData = loginResult.user;
        
        // Hole Club-ID und Federation
        const profileResult = await myTischtennisClient.getUserProfile(loginResult.cookie);
        
        if (profileResult.success) {
          account.clubId = profileResult.clubId;
          account.clubName = profileResult.clubName;
          account.fedNickname = profileResult.fedNickname;
        } else {
          console.error('[myTischtennisService] - Failed to get profile:', profileResult.error);
        }
      } else if (password) {
        account.lastLoginAttempt = now;
      }
      
      await account.save();

      // Save Playwright storage state separately so a missing DB column never breaks the main save.
      if (loginResult?.success && loginResult.storageState) {
        await this._savePlaywrightStorageState(account, loginResult.storageState);
      }
    } else {
      // Create new
      const accountData = {
        userId,
        email,
        savePassword,
        autoUpdateRatings: savePassword ? Boolean(autoUpdateRatings) : false,
        lastLoginAttempt: password ? now : null,
        lastLoginSuccess: loginResult?.success ? now : null
      };
      
      if (loginResult && loginResult.success) {
        accountData.accessToken = loginResult.accessToken;
        accountData.refreshToken = loginResult.refreshToken;
        accountData.expiresAt = loginResult.expiresAt;
        accountData.cookie = loginResult.cookie;
        accountData.userData = loginResult.user;
        
        // Hole Club-ID
        const profileResult = await myTischtennisClient.getUserProfile(loginResult.cookie);
        if (profileResult.success) {
          accountData.clubId = profileResult.clubId;
          accountData.clubName = profileResult.clubName;
          accountData.fedNickname = profileResult.fedNickname;
        }
      }
      
      account = await MyTischtennis.create(accountData);
      
      if (password && savePassword) {
        account.setPassword(password);
        await account.save();
      }

      if (loginResult?.success && loginResult.storageState) {
        await this._savePlaywrightStorageState(account, loginResult.storageState);
      }
    }

    return {
      id: account.id,
      email: account.email,
      savePassword: account.savePassword,
      autoUpdateRatings: account.autoUpdateRatings,
      lastLoginAttempt: account.lastLoginAttempt,
      lastLoginSuccess: account.lastLoginSuccess,
      lastUpdateRatings: account.lastUpdateRatings,
      expiresAt: account.expiresAt
    };
  }

  /**
   * Delete myTischtennis account
   */
  async deleteAccount(userId) {
    const deleted = await MyTischtennis.destroy({
      where: { userId }
    });
    return deleted > 0;
  }

  /**
   * Verify login with stored or provided credentials
   */
  async verifyLogin(userId, providedPassword = null) {
    const account = await MyTischtennis.findOne({ where: { userId } });
    
    if (!account) {
      throw new HttpError('Kein myTischtennis-Account verknüpft', 404);
    }

    const now = new Date();
    account.lastLoginAttempt = now;

    let password = providedPassword;
    const hasStoredPassword = !!(account.savePassword && account.encryptedPassword);
    const hasValidSession = !!(account.accessToken && account.cookie && account.expiresAt && account.expiresAt > Date.now() / 1000);
    
    // Wenn kein Passwort angegeben wurde, entweder gespeichertes Passwort oder bestehende Session verwenden
    if (!password) {
      if (hasStoredPassword) {
        password = account.getPassword();
        if (!password) {
          console.error('[myTischtennisService.verifyLogin] Could not decrypt stored password');
          throw new HttpError('Gespeichertes Passwort konnte nicht entschlüsselt werden. Bitte Passwort erneut eingeben.', 400);
        }
      } else if (hasValidSession) {
        // Prüfe, ob bestehende Session noch gültig ist
        const profileResult = await myTischtennisClient.getUserProfile(account.cookie);
        if (profileResult.success) {
          account.lastLoginSuccess = now;
          account.clubId = profileResult.clubId || account.clubId;
          account.clubName = profileResult.clubName || account.clubName;
          account.fedNickname = profileResult.fedNickname || account.fedNickname;
          await account.save();
          return {
            success: true,
            accessToken: account.accessToken,
            refreshToken: account.refreshToken,
            expiresAt: account.expiresAt,
            user: account.userData,
            clubId: account.clubId,
            clubName: account.clubName
          };
        }
        // Session ungültig -> Session-Daten zurücksetzen und speichern
        account.accessToken = null;
        account.refreshToken = null;
        account.cookie = null;
        account.expiresAt = null;
        await account.save();
        throw new HttpError('Kein Passwort gespeichert und Session abgelaufen. Bitte Passwort eingeben.', 400);
      } else {
        throw new HttpError('Kein Passwort gespeichert. Bitte geben Sie Ihr Passwort ein.', 400);
      }
    }

    // Login-Versuch mit Passwort
    console.log('[myTischtennisService.verifyLogin] Attempting login for user:', account.email);
    const loginResult = await myTischtennisClient.login(account.email, password);
    let effectiveLoginResult = loginResult;
    if (!effectiveLoginResult.success && effectiveLoginResult.requiresCaptcha) {
      console.log('[myTischtennisService.verifyLogin] CAPTCHA-Fehler, versuche Playwright-Fallback...');
      try {
        const savedStorageState = account.playwrightStorageState ?? null;
        const playwrightResult = await myTischtennisClient.loginWithBrowserAutomation(
          account.email, password, { savedStorageState }
        );
        if (playwrightResult.success) {
          effectiveLoginResult = playwrightResult;
        } else {
          console.warn('[myTischtennisService.verifyLogin] Playwright-Fallback fehlgeschlagen:', playwrightResult.error);
          const isSetupError = !!playwrightResult.requiresSetup || playwrightResult.status === 503;
          effectiveLoginResult = {
            success: false,
            error: playwrightResult.error || 'Playwright-Fallback fehlgeschlagen',
            requiresCaptcha: !isSetupError,
            status: isSetupError ? 503 : 400
          };
        }
      } catch (playwrightError) {
        console.warn('[myTischtennisService.verifyLogin] Playwright-Fallback Exception:', playwrightError?.message || playwrightError);
        const rawMessage = String(playwrightError?.message || playwrightError || '');
        const isSetupError = /Executable doesn't exist|download new browsers|playwright install/i.test(rawMessage);
        effectiveLoginResult = {
          success: false,
          error: isSetupError
            ? 'Playwright-Browser ist auf dem Server nicht installiert. Bitte "npx playwright install chromium" ausführen.'
            : `Playwright-Fallback Exception: ${playwrightError?.message || 'Unbekannter Fehler'}`,
          requiresCaptcha: !isSetupError,
          status: isSetupError ? 503 : 400
        };
      }
    }
    console.log('[myTischtennisService.verifyLogin] Login result:', {
      success: effectiveLoginResult.success,
      error: effectiveLoginResult.error,
      requiresCaptcha: effectiveLoginResult.requiresCaptcha
    });
    
    if (effectiveLoginResult.success) {
      account.lastLoginSuccess = now;
      account.accessToken = effectiveLoginResult.accessToken;
      account.refreshToken = effectiveLoginResult.refreshToken;
      account.expiresAt = effectiveLoginResult.expiresAt;
      account.cookie = effectiveLoginResult.cookie;
      account.userData = effectiveLoginResult.user;
      
      // Hole Club-ID und Federation
      const profileResult = await myTischtennisClient.getUserProfile(effectiveLoginResult.cookie);
      
      if (profileResult.success) {
        account.clubId = profileResult.clubId || account.clubId;
        account.clubName = profileResult.clubName || account.clubName;
        account.fedNickname = profileResult.fedNickname || account.fedNickname;
      } else {
        console.error('[myTischtennisService] - Failed to get profile:', profileResult.error);
      }
      
      await account.save();

      // Save Playwright storage state separately so a missing DB column never breaks the main save.
      if (effectiveLoginResult.storageState) {
        await this._savePlaywrightStorageState(account, effectiveLoginResult.storageState);
      }
      
      return {
        success: true,
        accessToken: effectiveLoginResult.accessToken,
        refreshToken: effectiveLoginResult.refreshToken,
        expiresAt: effectiveLoginResult.expiresAt,
        user: effectiveLoginResult.user,
        clubId: account.clubId,
        clubName: account.clubName
      };
    } else {
      // Prevent stale "success" from previously valid sessions after an explicit failed login attempt.
      account.accessToken = null;
      account.refreshToken = null;
      account.cookie = null;
      account.expiresAt = null;
      account.userData = null;
      await account.save(); // Save lastLoginAttempt
      const errorMessage = effectiveLoginResult.error || 'myTischtennis-Login fehlgeschlagen';
      // Verwende den Status-Code vom myTischtennisClient, falls vorhanden, sonst 401
      // Wenn CAPTCHA erforderlich ist, verwende 400 statt 401
      const statusCode = effectiveLoginResult.requiresCaptcha 
        ? 400 
        : (effectiveLoginResult.status && effectiveLoginResult.status >= 400 && effectiveLoginResult.status < 600 
          ? effectiveLoginResult.status 
          : 401);
      console.error('[myTischtennisService.verifyLogin] Login failed:', errorMessage, `(Status: ${statusCode})`, effectiveLoginResult.requiresCaptcha ? '(CAPTCHA erforderlich)' : '');
      if (effectiveLoginResult.requiresCaptcha) {
        throw new HttpError({ code: 'ERROR_MYTISCHTENNIS_CAPTCHA_REQUIRED', params: { message: errorMessage } }, statusCode);
      }
      throw new HttpError(errorMessage, statusCode);
    }
  }

  /**
   * Check if account is configured and ready
   */
  async checkAccountStatus(userId) {
    const account = await MyTischtennis.findOne({ where: { userId } });
    
    return {
      exists: !!account,
      hasEmail: !!account?.email,
      hasPassword: !!(account?.savePassword && account?.encryptedPassword),
      hasValidSession: !!(account?.accessToken && account?.cookie && account?.expiresAt && account?.expiresAt > Date.now() / 1000),
      needsConfiguration: !account || !account.email,
      needsPassword: !!account && (!account.savePassword || !account.encryptedPassword)
    };
  }
  
  /**
   * Get stored session for user (for authenticated API requests)
   */
  async getSession(userId) {
    const account = await MyTischtennis.findOne({ where: { userId } });
    
    if (!account) {
      throw new HttpError('Kein myTischtennis-Account verknüpft', 404);
    }
    
    // Check if session is valid
    if (!account.accessToken || !account.expiresAt || account.expiresAt < Date.now() / 1000) {
      throw new HttpError('Session abgelaufen. Bitte erneut einloggen.', 401);
    }
    
    return {
      accessToken: account.accessToken,
      refreshToken: account.refreshToken,
      cookie: account.cookie,
      expiresAt: account.expiresAt,
      userData: account.userData
    };
  }

  /**
   * Get update ratings history for user
   */
  async getUpdateHistory(userId) {
    const history = await MyTischtennisUpdateHistory.findAll({
      where: { userId },
      order: [['createdAt', 'DESC']], 
      limit: 50 // Letzte 50 Einträge
    });
    
    return history.map(entry => ({
      id: entry.id,
      success: entry.success,
      message: entry.message,
      errorDetails: entry.errorDetails,
      updatedCount: entry.updatedCount,
      executionTime: entry.executionTime,
      createdAt: entry.createdAt
    }));
  }

  /**
   * Log update ratings attempt
   */
  async logUpdateAttempt(userId, success, message, errorDetails = null, updatedCount = 0, executionTime = null) {
    try {
      await MyTischtennisUpdateHistory.create({
        userId,
        success,
        message,
        errorDetails,
        updatedCount,
        executionTime
      });

      // Update lastUpdateRatings in main table
      if (success) {
        await MyTischtennis.update(
          { lastUpdateRatings: new Date() },
          { where: { userId } } 
        );
      }
    } catch (error) {
      console.error('Error logging update attempt:', error);
    }
  }

  /**
   * Saves the Playwright browser storage state to the account.
   * Runs in its own try-catch so a missing DB column never blocks the main session save.
   */
  async _savePlaywrightStorageState(account, storageState) {
    try {
      account.playwrightStorageState = storageState;
      await account.save({ fields: ['playwrightStorageState'] });
      console.log('[myTischtennisService] Playwright storage state saved (session cached for future logins)');
    } catch (err) {
      // Column likely missing on this server — run:
      // ALTER TABLE my_tischtennis ADD COLUMN playwright_storage_state LONGTEXT NULL;
      console.warn('[myTischtennisService] Could not save playwright_storage_state (DB column missing?):', err.message);
    }
  }
}

export default new MyTischtennisService();

