import myTischtennisUrlParserService from '../services/myTischtennisUrlParserService.js';
import myTischtennisService from '../services/myTischtennisService.js';
import MemberService from '../services/memberService.js';
import autoFetchMatchResultsService from '../services/autoFetchMatchResultsService.js';
import apiLogService from '../services/apiLogService.js';
import axios from 'axios';
import ClubTeam from '../models/ClubTeam.js';
import League from '../models/League.js';
import Season from '../models/Season.js';
import User from '../models/User.js';
import HttpError from '../exceptions/HttpError.js';
import { devLog } from '../utils/logger.js';
import { hasUserClubAccess } from '../utils/userUtils.js';
import { randomUUID } from 'crypto';

const teamDataFetchJobs = new Map();
const TEAM_DATA_JOB_TTL_MS = 60 * 60 * 1000;

const cleanupFinishedTeamDataJobs = () => {
  const now = Date.now();
  for (const [jobId, job] of teamDataFetchJobs.entries()) {
    if (job.finishedAt && (now - job.finishedAt) > TEAM_DATA_JOB_TTL_MS) {
      teamDataFetchJobs.delete(jobId);
    }
  }
};

class MyTischtennisUrlController {
  async startFetchTeamDataJob(req, res, next) {
    try {
      const { clubTeamId } = req.body || {};
      if (!clubTeamId) {
        throw new HttpError('clubTeamId is required', 400);
      }

      cleanupFinishedTeamDataJobs();

      const jobId = randomUUID();
      const startedAt = Date.now();
      teamDataFetchJobs.set(jobId, {
        jobId,
        status: 'queued',
        startedAt,
        updatedAt: startedAt,
        finishedAt: null,
        clubTeamId,
        result: null,
        error: null
      });

      const authHeaders = {
        authcode: req.headers.authcode,
        userid: req.headers.userid
      };
      const internalPort = process.env.PORT || 3050;
      const internalUrl = `http://127.0.0.1:${internalPort}/api/mytischtennis/fetch-team-data`;

      // Background execution; response is returned immediately.
      (async () => {
        const job = teamDataFetchJobs.get(jobId);
        if (!job) return;
        job.status = 'running';
        job.updatedAt = Date.now();

        try {
          const response = await axios.post(
            internalUrl,
            { clubTeamId },
            {
              headers: authHeaders,
              timeout: 10 * 60 * 1000,
              validateStatus: () => true
            }
          );

          if (response.status >= 200 && response.status < 300 && response.data?.success) {
            job.status = 'completed';
            job.result = response.data;
            job.error = null;
          } else {
            job.status = 'failed';
            job.result = null;
            job.error = response.data?.error || response.data?.message || `Job failed with status ${response.status}`;
          }
        } catch (error) {
          job.status = 'failed';
          job.result = null;
          job.error = error?.message || String(error);
        } finally {
          job.updatedAt = Date.now();
          job.finishedAt = Date.now();
        }
      })();

      return res.status(202).json({
        success: true,
        jobId,
        status: 'queued'
      });
    } catch (error) {
      next(error);
    }
  }

  async getFetchTeamDataJobStatus(req, res, next) {
    try {
      const { jobId } = req.params;
      cleanupFinishedTeamDataJobs();
      const job = teamDataFetchJobs.get(jobId);

      if (!job) {
        throw new HttpError('Job not found', 404);
      }

      return res.status(200).json({
        success: true,
        job: {
          jobId: job.jobId,
          status: job.status,
          startedAt: job.startedAt,
          updatedAt: job.updatedAt,
          finishedAt: job.finishedAt,
          clubTeamId: job.clubTeamId,
          result: job.result,
          error: job.error
        }
      });
    } catch (error) {
      next(error);
    }
  }

  /**
   * Parse myTischtennis URL and return configuration data
   * POST /api/mytischtennis/parse-url
   * Body: { url: string }
   */
  async parseUrl(req, res, next) {
    try {
      const { url } = req.body;

      if (!url) {
        throw new HttpError('URL is required', 400);
      }

      // Validate URL
      if (!myTischtennisUrlParserService.isValidTeamUrl(url)) {
        throw new HttpError('Invalid myTischtennis URL format', 400);
      }

      // Parse URL
      const parsedData = myTischtennisUrlParserService.parseUrl(url);

      // Try to fetch additional data if user is authenticated
      const userIdOrEmail = req.headers.userid;
      let completeData = parsedData;

      if (userIdOrEmail) {
        // Get actual user ID
        let userId = userIdOrEmail;
        if (isNaN(userIdOrEmail)) {
          const user = await User.findOne({ where: { email: userIdOrEmail } });
          if (user) userId = user.id;
        }
        
        try {
          const account = await myTischtennisService.getAccount(userId);
          
          if (account && account.accessToken) {
            completeData = await myTischtennisUrlParserService.fetchTeamData(
              parsedData,
              account.cookie,
              account.accessToken
            );
          }
        } catch (error) {
          // Continue with parsed data only
        }
      }

      res.json({
        success: true,
        data: completeData
      });
    } catch (error) {
      next(error);
    }
  }

  /**
   * Configure team from myTischtennis URL
   * POST /api/mytischtennis/configure-team
   * Body: { url: string, clubTeamId: number, createLeague?: boolean, createSeason?: boolean }
   */
  async configureTeam(req, res, next) {
    try {
      const { url, clubTeamId, createLeague, createSeason } = req.body;
      const userIdOrEmail = req.headers.userid;

      if (!url || !clubTeamId) {
        throw new HttpError('URL and clubTeamId are required', 400);
      }

      // Get actual user ID
      let userId = userIdOrEmail;
      if (isNaN(userIdOrEmail)) {
        const user = await User.findOne({ where: { email: userIdOrEmail } });
        if (!user) {
          throw new HttpError('User not found', 404);
        }
        userId = user.id;
      }

      // Parse URL
      const parsedData = myTischtennisUrlParserService.parseUrl(url);

      // Try to fetch additional data
      let completeData = parsedData;
      const account = await myTischtennisService.getAccount(userId);
      
      if (account && account.accessToken) {
        try {
          completeData = await myTischtennisUrlParserService.fetchTeamData(
            parsedData,
            account.cookie,
            account.accessToken
          );
        } catch (error) {
        }
      }

      // Find or create season
      let season = await Season.findOne({
        where: { season: completeData.season }
      });

      if (!season && createSeason) {
        season = await Season.create({
          season: completeData.season
        });
      }

      if (!season) {
        throw new HttpError(`Season ${completeData.season} not found. Set createSeason=true to create it.`, 404);
      }

      // Find or create league
      const team = await ClubTeam.findByPk(clubTeamId);
      if (!team) {
        throw new HttpError('Club team not found', 404);
      }

      let league;
      
      // First, try to find existing league by name and season
      const leagueName = completeData.leagueName || completeData.groupname;
      league = await League.findOne({
        where: {
          name: leagueName,
          seasonId: season.id,
          clubId: team.clubId
        }
      });

      if (league) {
        devLog(`Found existing league: ${league.name} (ID: ${league.id})`);
        // Update myTischtennis fields
        await league.update({
          myTischtennisGroupId: completeData.groupId,
          association: completeData.association,
          groupname: completeData.groupname
        });
      } else if (team.leagueId) {
        // Team has a league assigned, update it
        league = await League.findByPk(team.leagueId);
        
        if (league) {
          devLog(`Updating team's existing league: ${league.name} (ID: ${league.id})`);
          await league.update({
            name: leagueName,
            myTischtennisGroupId: completeData.groupId,
            association: completeData.association,
            groupname: completeData.groupname
          });
        }
      } else if (createLeague) {
        // Create new league
        devLog(`Creating new league: ${leagueName}`);
        league = await League.create({
          name: leagueName,
          seasonId: season.id,
          clubId: team.clubId,
          myTischtennisGroupId: completeData.groupId,
          association: completeData.association,
          groupname: completeData.groupname
        });
      } else {
        throw new HttpError('League not found and team has no league assigned. Set createLeague=true to create one.', 400);
      }

      // Update team
      await team.update({
        myTischtennisTeamId: completeData.teamId,
        leagueId: league.id,
        seasonId: season.id
      });

      res.json({
        success: true,
        message: 'Team configured successfully',
        data: {
          team: {
            id: team.id,
            name: team.name,
            myTischtennisTeamId: completeData.teamId
          },
          league: {
            id: league.id,
            name: league.name,
            myTischtennisGroupId: completeData.groupId,
            association: completeData.association,
            groupname: completeData.groupname
          },
          season: {
            id: season.id,
            name: season.season
          },
          parsedData: completeData
        }
      });
    } catch (error) {
      next(error);
    }
  }

  /**
   * Manually fetch team data from myTischtennis
   * POST /api/mytischtennis/fetch-team-data
   * Body: { clubTeamId: number }
   */
  async fetchTeamData(req, res, next) {
    // Define outside of try/catch so catch has access
    let account = null;
    let team = null;
    let myTischtennisUrl = null;
    let requestStartTime = null;
    try {
      const { clubTeamId } = req.body;
      const userIdOrEmail = req.headers.userid;

      if (!clubTeamId) {
        throw new HttpError('clubTeamId is required', 400);
      }

      if (!userIdOrEmail) {
        throw new HttpError('User-ID fehlt. Bitte melden Sie sich an.', 401);
      }

      // Get actual user ID (userid header might be email address)
      let userId = userIdOrEmail;
      if (isNaN(userIdOrEmail)) {
        // It's an email, find the user
        const user = await User.findOne({ where: { email: userIdOrEmail } });
        if (!user) {
          throw new HttpError('User not found', 404);
        }
        userId = user.id;
      }

      // Get myTischtennis session (similar to memberService.updateRatingsFromMyTischtennis)
      let session;
      
      try {
        session = await myTischtennisService.getSession(userId);
      } catch (sessionError) {
        // Versuche automatischen Login mit gespeicherten Credentials
        try {
          // Check if account exists and has password
          const accountCheck = await myTischtennisService.getAccount(userId);
          if (!accountCheck) {
            throw new Error('MyTischtennis-Account nicht gefunden');
          }
          
          if (!accountCheck.encryptedPassword) {
            throw new Error('Kein Passwort gespeichert. Bitte melden Sie sich in den MyTischtennis-Einstellungen an und speichern Sie Ihr Passwort.');
          }
          
          await myTischtennisService.verifyLogin(userId);
          session = await myTischtennisService.getSession(userId);
        } catch (loginError) {
          const errorMessage = loginError.message || 'Automatischer Login fehlgeschlagen';
          throw new HttpError(`MyTischtennis-Session abgelaufen und automatischer Login fehlgeschlagen: ${errorMessage}. Bitte melden Sie sich in den MyTischtennis-Einstellungen an.`, 401);
        }
      }
      
      // Get account data (for clubId, etc.)
      account = await myTischtennisService.getAccount(userId);
      
      if (!account) {
        throw new HttpError('MyTischtennis-Account nicht verknüpft. Bitte verknüpfen Sie Ihren Account in den MyTischtennis-Einstellungen.', 404);
      }
      
      
      // Get team with league and season
      team = await ClubTeam.findByPk(clubTeamId, {
        include: [
          {
            model: League,
            as: 'league',
            include: [
              {
                model: Season,
                as: 'season'
              }
            ]
          }
        ]
      });

      if (!team) {
        throw new HttpError(`Team mit ID ${clubTeamId} nicht gefunden`, 404);
      }

      // Verbesserte Validierung mit detaillierten Fehlermeldungen
      if (!team.myTischtennisTeamId) {
        throw new HttpError(`Team "${team.name}" (interne ID: ${team.id}) ist nicht für myTischtennis konfiguriert: myTischtennisTeamId fehlt. Bitte konfigurieren Sie das Team zuerst über die MyTischtennis-URL.`, 400);
      }
      
      if (!team.league) {
        throw new HttpError('Team ist keiner Liga zugeordnet. Bitte ordnen Sie das Team einer Liga zu.', 400);
      }
      
      if (!team.league.myTischtennisGroupId) {
        throw new HttpError('Liga ist nicht für myTischtennis konfiguriert: myTischtennisGroupId fehlt. Bitte konfigurieren Sie die Liga zuerst über die MyTischtennis-URL.', 400);
      }

      // Validate season before proceeding
      if (!team.league.season || !team.league.season.season) {
        throw new HttpError('Liga ist keiner Saison zugeordnet. Bitte ordnen Sie die Liga einer Saison zu.', 400);
      }

      // Build the URL that will be used - do this early so we can log it even if errors occur
      const seasonFull = team.league.season.season;
      const seasonParts = seasonFull.split('/');
      const seasonShort = seasonParts.length === 2 
        ? `${seasonParts[0].slice(-2)}/${seasonParts[1].slice(-2)}` 
        : seasonFull;
      const seasonStr = seasonShort.replace('/', '--');
      const teamnameEncoded = encodeURIComponent(team.name.replace(/\s/g, '_'));
      myTischtennisUrl = `https://www.mytischtennis.de/click-tt/${team.league.association}/${seasonStr}/ligen/${team.league.groupname}/gruppe/${team.league.myTischtennisGroupId}/mannschaft/${team.myTischtennisTeamId}/${teamnameEncoded}/spielerbilanzen/gesamt`;

      // Log the request to myTischtennis BEFORE making the call
      // This ensures we always see what WILL BE sent, even if the call fails
      requestStartTime = Date.now();
      try {
        await apiLogService.logRequest({
          userId: account.userId,
          method: 'GET',
          path: myTischtennisUrl.replace('https://www.mytischtennis.de', ''),
          statusCode: null,
          requestBody: JSON.stringify({
            url: myTischtennisUrl,
            myTischtennisTeamId: team.myTischtennisTeamId,
            clubTeamId: team.id,
            teamName: team.name,
            leagueName: team.league.name,
            association: team.league.association,
            groupId: team.league.myTischtennisGroupId,
            groupname: team.league.groupname,
            season: seasonFull
          }),
          responseBody: null,
          executionTime: null,
          errorMessage: 'Request wird ausgeführt...',
          logType: 'api_request',
          schedulerJobType: 'mytischtennis_fetch'
        });
      } catch (logError) {
        // Silent fail - logging errors shouldn't break the request
      }

      // Fetch data for this specific team
      // Note: fetchTeamResults will also log and update with actual response
      const result = await autoFetchMatchResultsService.fetchTeamResults(
        {
          userId: account.userId,
          email: account.email,
          cookie: session.cookie,
          accessToken: session.accessToken,
          expiresAt: session.expiresAt,
          getPassword: () => null // Not needed for manual fetch
        },
        team
      );

        // Also fetch and update league table data
        let tableUpdateResult = null;
      try {
        await autoFetchMatchResultsService.fetchAndUpdateLeagueTable(account.userId, team.league.id);
          tableUpdateResult = 'League table updated successfully';
        } catch (error) {
          tableUpdateResult = 'League table update failed: ' + error.message;
          // Don't fail the entire request if table update fails
        }

        // Additionally update (Q)TTR ratings for the club
        let ratingsUpdate = null;
        try {
          // Use already resolved userId instead of authcode to avoid header dependency
          const ratingsResult = await MemberService.updateRatingsFromMyTischtennisByUserId(userId, team.clubId);
          ratingsUpdate = ratingsResult?.response?.message || `Ratings update status: ${ratingsResult?.status}`;
        } catch (ratingsErr) {
          ratingsUpdate = 'Ratings update failed: ' + (ratingsErr.message || String(ratingsErr));
        }

        res.json({
          success: true,
          message: `${result.fetchedCount} Datensätze abgerufen und verarbeitet`,
          data: {
            fetchedCount: result.fetchedCount,
            teamName: team.name,
            tableUpdate: tableUpdateResult,
            ratingsUpdate
          }
        });
      } catch (error) {
        
        // Update log with error information if we got far enough to build the URL
        if (myTischtennisUrl && account && team) {
          const requestExecutionTime = requestStartTime ? (Date.now() - requestStartTime) : null;
          try {
            await apiLogService.logRequest({
              userId: account.userId,
              method: 'GET',
              path: myTischtennisUrl.replace('https://www.mytischtennis.de', ''),
              statusCode: 0,
              requestBody: JSON.stringify({
                url: myTischtennisUrl,
                myTischtennisTeamId: team.myTischtennisTeamId,
                clubTeamId: team.id,
                teamName: team.name,
                leagueName: team.league?.name,
                association: team.league?.association,
                groupname: team.league?.groupname,
                groupId: team.league?.myTischtennisGroupId
              }),
              responseBody: null,
              executionTime: requestExecutionTime,
              errorMessage: error.message || String(error),
              logType: 'api_request',
              schedulerJobType: 'mytischtennis_fetch'
            });
          } catch (logError) {
            // Silent fail - logging errors shouldn't break the request
          }
        }
        
        // Normalize HTTP status code (guard against strings)
        const rawCode = error && (error.statusCode != null ? error.statusCode : error.status);
        const parsed = Number(rawCode);
        const status = Number.isInteger(parsed) && parsed >= 100 && parsed <= 599 ? parsed : 500;
        const debug = {
          message: error.message || String(error),
          name: error.name,
          stack: (process.env.NODE_ENV === 'dev' || process.env.NODE_ENV === 'development') ? (error.stack || null) : undefined,
          team: team ? { id: team.id, name: team.name } : null,
          league: team && team.league ? { id: team.league.id, name: team.league.name, association: team.league.association, groupId: team.league.myTischtennisGroupId, groupname: team.league.groupname } : null,
          url: typeof myTischtennisUrl !== 'undefined' ? myTischtennisUrl : null
        };
        try {
          if (!res.headersSent) {
            // Spezieller Fall: myTischtennis-Reauth nötig → nicht 401 an FE senden, um App-Logout zu vermeiden
            const isMyTischtennisAuthIssue = status === 401 && /MyTischtennis-Session abgelaufen|Automatischer Login fehlgeschlagen|Passwort gespeichert/i.test(debug.message || '');
            if (isMyTischtennisAuthIssue) {
              return res.status(200).json({ success: false, error: debug.message, debug, needsMyTischtennisReauth: true });
            }
            res.status(status).json({ success: false, error: debug.message, debug });
          }
        } catch (writeErr) {
          // Fallback, falls Headers schon gesendet wurden
          // eslint-disable-next-line no-console
          console.error('[fetchTeamData] Response write failed:', writeErr);
        }
      }
  }

  /**
   * Get myTischtennis URL for a team
   * GET /api/mytischtennis/team-url/:teamId
   */
  async getTeamUrl(req, res, next) {
    try {
      const { teamId } = req.params;

      const team = await ClubTeam.findByPk(teamId, {
        include: [
          {
            model: League,
            as: 'league',
            include: [
              {
                model: Season,
                as: 'season'
              }
            ]
          }
        ]
      });

      if (!team) {
        throw new HttpError('Team not found', 404);
      }

      if (!team.myTischtennisTeamId || !team.league || !team.league.myTischtennisGroupId) {
        throw new HttpError('Team is not configured for myTischtennis', 400);
      }

      const url = myTischtennisUrlParserService.buildUrl({
        association: team.league.association,
        season: team.league.season?.season,
        groupname: team.league.groupname,
        groupId: team.league.myTischtennisGroupId,
        teamId: team.myTischtennisTeamId,
        teamname: team.name
      });

      res.json({
        success: true,
        url
      });
    } catch (error) {
      next(error);
    }
  }

  /**
   * Configure league from myTischtennis table URL
   * POST /api/mytischtennis/configure-league
   * Body: { url: string, clubId: number, createSeason?: boolean }
   */
  async configureLeague(req, res, next) {
    try {
      const { url, createSeason, clubId } = req.body;
      const userIdOrEmail = req.headers.userid;

      if (!url || !clubId) {
        throw new HttpError('URL and clubId are required', 400);
      }

      let userId = userIdOrEmail;
      if (isNaN(userIdOrEmail)) {
        const user = await User.findOne({ where: { email: userIdOrEmail } });
        if (!user) {
          throw new HttpError('User not found', 404);
        }
        userId = user.id;
      }

      const normalizedClubId = Number.parseInt(clubId, 10);
      if (!Number.isInteger(normalizedClubId) || normalizedClubId <= 0) {
        throw new HttpError('clubId must be a valid number', 400);
      }

      const hasAccess = await hasUserClubAccess(userId, normalizedClubId);
      if (!hasAccess) {
        throw new HttpError('Keine Berechtigung für diesen Verein', 403);
      }

      // Parse URL
      const parsedData = myTischtennisUrlParserService.parseUrl(url);

      if (parsedData.urlType !== 'table') {
        throw new HttpError('URL must be a table URL (not a team URL)', 400);
      }

      // Find or create season
      let season = await Season.findOne({
        where: { season: parsedData.season }
      });

      if (!season && createSeason) {
        season = await Season.create({
          season: parsedData.season,
          startDate: new Date(),
          endDate: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000) // 1 Jahr später
        });
      }

      // Find or create league
      let league = await League.findOne({
        where: {
          clubId: normalizedClubId,
          myTischtennisGroupId: parsedData.groupId,
          association: parsedData.association
        }
      });

      if (!league) {
        league = await League.create({
          name: parsedData.groupnameOriginal, // Verwende die originale URL-kodierte Version
          clubId: normalizedClubId,
          myTischtennisGroupId: parsedData.groupId,
          association: parsedData.association,
          groupname: parsedData.groupnameOriginal, // Verwende die originale URL-kodierte Version
          seasonId: season?.id || null
        });
      } else {
        // Update existing league - aber nur wenn es sich wirklich geändert hat
        if (league.name !== parsedData.groupnameOriginal) {
          league.name = parsedData.groupnameOriginal;
          league.groupname = parsedData.groupnameOriginal;
        }
        league.seasonId = season?.id || league.seasonId;
        await league.save();
      }

      res.json({
        success: true,
        message: 'League configured successfully',
        data: {
          league: {
            id: league.id,
            name: league.name,
            myTischtennisGroupId: league.myTischtennisGroupId,
            association: league.association,
            groupname: league.groupname
          },
          season: season ? {
            id: season.id,
            name: season.season
          } : null,
          parsedData
        }
      });
    } catch (error) {
      next(error);
    }
  }
}

export default new MyTischtennisUrlController();
