import myTischtennisService from '../services/myTischtennisService.js';
import myTischtennisSessionService from '../services/myTischtennisSessionService.js';
import myTischtennisProxyService from '../services/myTischtennisProxyService.js';
import HttpError from '../exceptions/HttpError.js';
import axios from 'axios';
import myTischtennisClient from '../clients/myTischtennisClient.js';

class MyTischtennisController {
  /**
   * GET /api/mytischtennis/account
   * Get current user's myTischtennis account
   */
  async getAccount(req, res, next) {
    try {
      const userId = req.user.id;
      const account = await myTischtennisService.getAccount(userId);
      
      if (!account) {
        return res.status(200).json({ account: null });
      }
      
      res.status(200).json({ account });
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /api/mytischtennis/status
   * Check account configuration status
   */
  async getStatus(req, res, next) {
    try {
      const userId = req.user.id;
      const status = await myTischtennisService.checkAccountStatus(userId);
      res.status(200).json(status);
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /api/mytischtennis/login-form
   * Parsed login form data from mytischtennis.de
   */
  async getLoginForm(req, res, next) {
    try {
      const myTischtennisClient = (await import('../clients/myTischtennisClient.js')).default;
      const result = await myTischtennisClient.getLoginPage();

      if (!result.success) {
        throw new HttpError('Login-Formular konnte nicht geladen werden', 502);
      }

      const publicFields = (result.fields || [])
        .filter((field) => ['email', 'password'].includes(field.type) || field.name === 'email' || field.name === 'password')
        .map((field) => ({
          name: field.name,
          id: field.id,
          type: field.type,
          placeholder: field.placeholder || null,
          required: !!field.required,
          autocomplete: field.autocomplete || null,
          minlength: field.minlength ? Number(field.minlength) : null
        }));

      res.status(200).json({
        success: true,
        form: {
          action: result.loginAction,
          fields: publicFields
        },
        captcha: {
          required: !!result.requiresCaptcha,
          siteKey: result.captchaSiteKey || null,
          puzzleEndpoint: result.captchaPuzzleEndpoint || null,
          solutionField: result.captchaSolutionField || 'captcha'
        }
      });
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /api/mytischtennis/account
   * Create or update myTischtennis account
   */
  async upsertAccount(req, res, next) {
    try {
      const userId = req.user.id;
      const { email, password, savePassword, userPassword } = req.body;
      const hasAutoUpdateRatings = Object.prototype.hasOwnProperty.call(req.body, 'autoUpdateRatings');
      const autoUpdateRatings = hasAutoUpdateRatings ? req.body.autoUpdateRatings : undefined;

      if (!email) {
        throw new HttpError('E-Mail-Adresse erforderlich', 400);
      }

      // Wenn ein Passwort gesetzt wird, muss das App-Passwort angegeben werden
      if (password && !userPassword) {
        throw new HttpError('App-Passwort erforderlich zum Setzen des myTischtennis-Passworts', 400);
      }

      const account = await myTischtennisService.upsertAccount(
        userId,
        email,
        password,
        savePassword || false,
        autoUpdateRatings,
        userPassword
      );

      res.status(200).json({ 
        message: 'myTischtennis-Account erfolgreich gespeichert',
        account 
      });
    } catch (error) {
      next(error);
    }
  }

  /**
   * DELETE /api/mytischtennis/account
   * Delete myTischtennis account
   */
  async deleteAccount(req, res, next) {
    try {
      const userId = req.user.id;
      const deleted = await myTischtennisService.deleteAccount(userId);
      
      if (!deleted) {
        throw new HttpError('Kein myTischtennis-Account gefunden', 404);
      }

      res.status(200).json({ message: 'myTischtennis-Account gelöscht' });
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /api/mytischtennis/verify
   * Verify login credentials
   */
  async verifyLogin(req, res, next) {
    try {
      const userId = req.user.id;
      const { password } = req.body;

      const result = await myTischtennisService.verifyLogin(userId, password);

      res.status(200).json({
        message: 'Login erfolgreich',
        success: true,
        accessToken: result.accessToken,
        expiresAt: result.expiresAt,
        clubId: result.clubId,
        clubName: result.clubName
      });
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /api/mytischtennis/session
   * Get stored session data for authenticated requests
   */
  async getSession(req, res, next) {
    try {
      const userId = req.user.id;
      const session = await myTischtennisService.getSession(userId);

      res.status(200).json({ session });
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /api/mytischtennis/update-history
   * Get update ratings history
   */
  async getUpdateHistory(req, res, next) {
    try {
      const userId = req.user.id;
      const history = await myTischtennisService.getUpdateHistory(userId);
      res.status(200).json({ history });
    } catch (error) {
      next(error);
    }
  }

  /**
   * Get fetch logs for current user
   */
  async getFetchLogs(req, res, next) {
    try {
      const { userid: userIdOrEmail } = req.headers;
      
      // Convert email to userId if needed
      let userId = userIdOrEmail;
      if (isNaN(userIdOrEmail)) {
        const User = (await import('../models/User.js')).default;
        const user = await User.findOne({ where: { email: userIdOrEmail } });
        if (!user) {
          return res.status(404).json({ error: 'User not found' });
        }
        userId = user.id;
      }

      const fetchLogService = (await import('../services/myTischtennisFetchLogService.js')).default;
      const logs = await fetchLogService.getFetchLogs(userId, {
        limit: req.query.limit ? parseInt(req.query.limit) : 50,
        fetchType: req.query.type
      });

      res.status(200).json({ logs });
    } catch (error) {
      next(error);
    }
  }

  /**
   * Get latest successful fetches for each type
   */
  async getLatestFetches(req, res, next) {
    try {
      const { userid: userIdOrEmail } = req.headers;
      
      // Convert email to userId if needed
      let userId = userIdOrEmail;
      if (isNaN(userIdOrEmail)) {
        const User = (await import('../models/User.js')).default;
        const user = await User.findOne({ where: { email: userIdOrEmail } });
        if (!user) {
          return res.status(404).json({ error: 'User not found' });
        }
        userId = user.id;
      }

      const fetchLogService = (await import('../services/myTischtennisFetchLogService.js')).default;
      const latestFetches = await fetchLogService.getLatestSuccessfulFetches(userId);

      res.status(200).json({ latestFetches });
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /api/mytischtennis/login-page
   * Proxy für Login-Seite (für iframe)
   * Lädt die Login-Seite von mytischtennis.de und modifiziert sie, sodass Form-Submissions über unseren Proxy gehen
   * Authentifizierung ist optional - Token kann als Query-Parameter übergeben werden
   */
  async getLoginPage(req, res, next) {
    try {
      // Versuche, userId aus Token zu bekommen (optional)
      let userId = null;
      const token = req.query.token || req.headers['authorization']?.split(' ')[1] || req.headers['authcode'];
      if (token) {
        try {
          const jwt = (await import('jsonwebtoken')).default;
          const decoded = jwt.verify(token, process.env.JWT_SECRET);
          userId = decoded.userId;
        } catch (err) {
          // Token ungültig - ignorieren
        }
      }
      
      // Speichere userId im Request für submitLogin
      req.userId = userId;
      
      // Lade die Login-Seite von mytischtennis.de
      const response = await axios.get(`${myTischtennisProxyService.getOrigin()}/login?next=%2F`, {
        headers: {
          'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
          'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
          'Accept-Language': 'de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7'
        },
        maxRedirects: 5,
        validateStatus: () => true // Akzeptiere alle Status-Codes
      });

      // Setze Cookies aus der Response
      const setCookieHeaders = response.headers['set-cookie'];
      if (setCookieHeaders) {
        res.setHeader('Set-Cookie', setCookieHeaders);
      }

      // Modifiziere HTML: Ändere Form-Action auf unseren Proxy
      let html = response.data;
      if (typeof html === 'string') {
        // Füge Token als Hidden-Input hinzu, damit submitLogin die userId bekommt
        const tokenInput = userId ? `<input type="hidden" name="__token" value="${token}" />` : '';
        
        // Ersetze Form-Action URLs und füge Token-Input hinzu
        html = html.replace(
          /(<form[^>]*action="[^"]*\/login[^"]*"[^>]*>)/g,
          `$1${tokenInput}`
        );
        html = html.replace(
          /action="([^"]*\/login[^"]*)"/g,
          'action="/api/mytischtennis/login-submit"'
        );
        // Ersetze auch relative URLs
        html = html.replace(
          /action="\/login/g,
          'action="/api/mytischtennis/login-submit'
        );
        html = myTischtennisProxyService.rewriteContent(html);

        // MyTischtennis bootet eine große React-App, die im Proxy-Kontext häufig mit
        // Runtime-Fehlern abstürzt ("Da ist etwas schiefgelaufen"). Für den iframe-Login
        // reicht die serverseitig gerenderte Form aus; deshalb Bootstrap-Skripte entfernen.
        html = html.replace(/<script\b[^>]*type=(?:"|')module(?:"|')[^>]*>[\s\S]*?<\/script>/gi, '');
        html = html.replace(/<script\b[^>]*src=(?:"|')[^"']*\/build\/[^"']*(?:"|')[^>]*>\s*<\/script>/gi, '');
        html = html.replace(/<link\b[^>]*rel=(?:"|')modulepreload(?:"|')[^>]*>/gi, '');
      }

      // Setze Content-Type
      res.setHeader('Content-Type', response.headers['content-type'] || 'text/html; charset=utf-8');

      // Sende den modifizierten HTML-Inhalt
      res.status(response.status).send(html);
    } catch (error) {
      console.error('Fehler beim Laden der Login-Seite:', error);
      next(error);
    }
  }

  /**
   * GET /api/mytischtennis/proxy/*
   * Same-Origin-Proxy für mytischtennis Build-/Font-/Captcha-Ressourcen
   */
  async proxyRemote(req, res, next) {
    try {
      const proxyPath = req.params[0] || '';
      const queryString = new URLSearchParams(req.query || {}).toString();
      const targetUrl = `${myTischtennisProxyService.getOrigin()}/${proxyPath}${queryString ? `?${queryString}` : ''}`;

      const upstream = await axios.get(targetUrl, {
        responseType: 'arraybuffer',
        headers: {
          'User-Agent': req.headers['user-agent'] || 'Mozilla/5.0',
          'Accept': req.headers.accept || '*/*',
          'Accept-Language': req.headers['accept-language'] || 'de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7',
          ...(req.headers.cookie ? { 'Cookie': req.headers.cookie } : {})
        },
        validateStatus: () => true
      });

      // Wichtige Header durchreichen
      const passthroughHeaders = ['content-type', 'cache-control', 'etag', 'last-modified', 'expires'];
      for (const headerName of passthroughHeaders) {
        const value = upstream.headers[headerName];
        if (value) {
          res.setHeader(headerName, value);
        }
      }
      if (upstream.headers['set-cookie']) {
        res.setHeader('Set-Cookie', upstream.headers['set-cookie']);
      }

      const contentType = String(upstream.headers['content-type'] || '').toLowerCase();
      const isTextLike = /(text\/|javascript|json|xml|svg)/.test(contentType);

      if (isTextLike) {
        const asText = Buffer.from(upstream.data).toString('utf-8');
        const rewritten = myTischtennisProxyService.rewriteContent(asText);
        return res.status(upstream.status).send(rewritten);
      }

      return res.status(upstream.status).send(upstream.data);
    } catch (error) {
      console.error('Fehler beim Proxy von mytischtennis-Ressourcen:', error.message);
      next(error);
    }
  }

  /**
   * POST /api/mytischtennis/login-submit
   * Proxy für Login-Form-Submission
   * Leitet den Login-Request durch, damit Cookies im Backend-Kontext bleiben
   * Authentifizierung ist optional - iframe kann keinen Token mitsenden
   */
  async submitLogin(req, res, next) {
    try {
      // Versuche, userId aus Token zu bekommen (aus Query-Parameter oder Hidden-Input)
      let userId = null;
      const token = req.query.token || req.body.__token || req.headers['authorization']?.split(' ')[1] || req.headers['authcode'];
      if (token) {
        try {
          const jwt = (await import('jsonwebtoken')).default;
          const decoded = jwt.verify(token, process.env.JWT_SECRET);
          userId = decoded.userId;
        } catch (err) {
          // Token ungültig - ignorieren
        }
      }
      
      // Entferne __token aus req.body, damit es nicht an mytischtennis.de gesendet wird
      if (req.body.__token) {
        delete req.body.__token;
      }

      // Hole Cookies aus dem Request (wird auch für CAPTCHA-Fallback benötigt)
      const cookies = req.headers.cookie || '';

      // Normalisiere Payload
      const payload = { ...(req.body || {}) };
      const mask = (v) => (typeof v === 'string' && v.length > 12 ? `${v.slice(0, 12)}...(${v.length})` : v);

      // Falls captcha im Browser-Kontext nicht gesetzt wurde, versuche serverseitigen Fallback
      if (!payload.captcha) {
        try {
          const loginPageResponse = await axios.get('https://www.mytischtennis.de/login?next=%2F', {
            headers: {
              'Cookie': cookies,
              'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
              'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
              'Accept-Language': 'de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7',
              'Referer': 'https://www.mytischtennis.de/'
            },
            validateStatus: () => true
          });

          const html = typeof loginPageResponse.data === 'string' ? loginPageResponse.data : '';
          const siteKeyMatch = html.match(/data-sitekey=(?:"([^"]+)"|'([^']+)')/i);
          const puzzleEndpointMatch = html.match(/data-puzzle-endpoint=(?:"([^"]+)"|'([^']+)')/i);
          const siteKey = siteKeyMatch ? (siteKeyMatch[1] || siteKeyMatch[2]) : null;
          const puzzleEndpoint = puzzleEndpointMatch ? (puzzleEndpointMatch[1] || puzzleEndpointMatch[2]) : null;

          if (siteKey && puzzleEndpoint) {
            const puzzleResponse = await axios.get(`${puzzleEndpoint}?sitekey=${encodeURIComponent(siteKey)}`, {
              headers: {
                'Cookie': cookies,
                'Accept': '*/*',
                'Origin': 'https://www.mytischtennis.de',
                'Referer': 'https://www.mytischtennis.de/'
              },
              validateStatus: () => true
            });

            if (puzzleResponse.status === 200 && typeof puzzleResponse.data === 'string' && puzzleResponse.data.trim()) {
              payload.captcha = puzzleResponse.data.trim();
              payload.captcha_clicked = 'true';
            }
          }
        } catch (captchaFallbackError) {
          console.warn('[submitLogin] CAPTCHA-Fallback fehlgeschlagen:', captchaFallbackError.message);
        }
      }

      // Wenn captcha vorhanden ist, als bestätigt markieren
      if (payload.captcha && !payload.captcha_clicked) {
        payload.captcha_clicked = 'true';
      }

      console.log('[submitLogin] Incoming payload fields:', {
        keys: Object.keys(payload),
        hasEmail: !!payload.email,
        hasPassword: !!payload.password,
        xsrf: mask(payload.xsrf),
        captchaClicked: payload.captcha_clicked,
        captcha: mask(payload.captcha)
      });

      // Form-Daten sauber als x-www-form-urlencoded serialisieren
      const formData = new URLSearchParams();
      for (const [key, value] of Object.entries(payload)) {
        if (value !== undefined && value !== null) {
          formData.append(key, String(value));
        }
      }
      
      // Leite den Login-Request an mytischtennis.de weiter
      const response = await axios.post(
        'https://www.mytischtennis.de/login?next=%2F&_data=routes%2F_auth%2B%2Flogin',
        formData.toString(),
        {
          headers: {
            'Cookie': cookies,
            'Content-Type': 'application/x-www-form-urlencoded',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
            'Accept': '*/*',
            'Referer': 'https://www.mytischtennis.de/login?next=%2F'
          },
          maxRedirects: 0,
          validateStatus: () => true
        }
      );

      console.log('[submitLogin] Upstream response:', {
        status: response.status,
        hasSetCookie: Array.isArray(response.headers['set-cookie']) && response.headers['set-cookie'].length > 0,
        bodyPreview: typeof response.data === 'string'
          ? response.data.slice(0, 220)
          : JSON.stringify(response.data || {}).slice(0, 220)
      });

      // Falls CAPTCHA-Bestätigung im Proxy-Flow fehlschlägt:
      // Fallback auf echten Browser-Login (Playwright), dann Session direkt speichern.
      const upstreamBody = typeof response.data === 'string' ? response.data : JSON.stringify(response.data || {});
      const isCaptchaFailure = response.status === 400
        && (upstreamBody.includes('Captcha-Bestätigung fehlgeschlagen') || upstreamBody.includes('Captcha-Bestätigung ist erforderlich'));

      if (isCaptchaFailure && userId && payload.email && payload.password) {
        console.log('[submitLogin] CAPTCHA-Fehler erkannt, starte Playwright-Fallback...');
        const browserLogin = await myTischtennisClient.loginWithBrowserAutomation(payload.email, payload.password);

        if (browserLogin.success && browserLogin.cookie) {
          await myTischtennisSessionService.saveSessionFromCookie(userId, browserLogin.cookie);
          return res.status(200).send(
            '<!doctype html><html><body><p>Login erfolgreich. Fenster kann geschlossen werden.</p></body></html>'
          );
        }

        console.warn('[submitLogin] Playwright-Fallback fehlgeschlagen:', browserLogin.error);
      }

      // Setze Cookies aus der Response
      const setCookieHeaders = response.headers['set-cookie'];
      if (setCookieHeaders) {
        res.setHeader('Set-Cookie', setCookieHeaders);
      }

      // Setze andere relevante Headers
      if (response.headers['content-type']) {
        res.setHeader('Content-Type', response.headers['content-type']);
      }
      if (response.headers['location']) {
        res.setHeader('Location', response.headers['location']);
      }

      // Prüfe, ob Login erfolgreich war (durch Prüfung der Cookies)
      const authCookie = setCookieHeaders?.find(cookie => cookie.startsWith('sb-10-auth-token='));
      if (authCookie && userId) {
        // Login erfolgreich - speichere Session (nur wenn userId vorhanden)
        await myTischtennisSessionService.saveSessionFromCookie(userId, authCookie);
      }

      // Sende Response weiter
      res.status(response.status).send(response.data);
    } catch (error) {
      console.error('Fehler beim Login-Submit:', error);
      next(error);
    }
  }

  /**
   * POST /api/mytischtennis/extract-session
   * Extrahiere Session nach Login im iframe
   * Versucht, die Session-Daten aus den Cookies zu extrahieren
   * Authentifizierung ist optional - iframe kann keinen Token mitsenden
   */
  async extractSession(req, res, next) {
    try {
      // Versuche, userId aus Token zu bekommen (optional)
      let userId = req.user?.id;
      
      // Falls kein Token vorhanden, versuche userId aus Account zu bekommen (falls E-Mail bekannt)
      if (!userId) {
        // Kann nicht ohne Authentifizierung arbeiten - Session kann nicht gespeichert werden
        return res.status(401).json({ 
          error: 'Authentifizierung erforderlich zum Speichern der Session' 
        });
      }
      
      // Hole die Cookies aus dem Request
      const cookies = req.headers.cookie || '';
      
      // Versuche, die Session zu verifizieren, indem wir einen Request mit den Cookies machen
      const response = await axios.get('https://www.mytischtennis.de/?_data=root', {
        headers: {
          'Cookie': cookies,
          'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
          'Accept': 'application/json'
        },
        validateStatus: () => true
      });

      // Prüfe, ob wir eingeloggt sind (durch Prüfung der Response)
      if (response.status === 200 && response.data?.userProfile) {
        // Session erfolgreich - speichere die Daten
        const account = await myTischtennisService.getAccount(userId);
        if (!account) {
          throw new HttpError('Kein myTischtennis-Account verknüpft', 404);
        }

        // Extrahiere Cookie-String
        const cookieString = cookies.split(';').find(c => c.trim().startsWith('sb-10-auth-token='));
        if (!cookieString) {
          throw new HttpError('Kein Auth-Token in Cookies gefunden', 400);
        }

        // Parse Token aus Cookie
        const tokenMatch = cookieString.match(/sb-10-auth-token=base64-([^;]+)/);
        if (!tokenMatch) {
          throw new HttpError('Token-Format ungültig', 400);
        }

        const base64Token = tokenMatch[1];
        const decodedToken = Buffer.from(base64Token, 'base64').toString('utf-8');
        const tokenData = JSON.parse(decodedToken);

        // Aktualisiere Account mit Session-Daten
        const MyTischtennis = (await import('../models/MyTischtennis.js')).default;
        const myTischtennisAccount = await MyTischtennis.findOne({ where: { userId } });
        
        if (myTischtennisAccount) {
          myTischtennisAccount.accessToken = tokenData.access_token;
          myTischtennisAccount.refreshToken = tokenData.refresh_token;
          myTischtennisAccount.expiresAt = tokenData.expires_at;
          myTischtennisAccount.cookie = cookieString.trim();
          myTischtennisAccount.userData = tokenData.user;
          myTischtennisAccount.lastLoginSuccess = new Date();
          myTischtennisAccount.lastLoginAttempt = new Date();
          
          // Hole Club-Informationen
          const myTischtennisClient = (await import('../clients/myTischtennisClient.js')).default;
          const profileResult = await myTischtennisClient.getUserProfile(cookieString.trim());
          if (profileResult.success) {
            myTischtennisAccount.clubId = profileResult.clubId;
            myTischtennisAccount.clubName = profileResult.clubName;
            myTischtennisAccount.fedNickname = profileResult.fedNickname;
          }
          
          await myTischtennisAccount.save();
        }

        res.status(200).json({
          success: true,
          message: 'Session erfolgreich extrahiert und gespeichert'
        });
      } else {
        throw new HttpError('Nicht eingeloggt oder Session ungültig', 401);
      }
    } catch (error) {
      console.error('Fehler beim Extrahieren der Session:', error);
      next(error);
    }
  }
}

export default new MyTischtennisController();

