import myTischtennisService from './myTischtennisService.js';
import myTischtennisFetchLogService from './myTischtennisFetchLogService.js';
import apiLogService from './apiLogService.js';
import myTischtennisClient from '../clients/myTischtennisClient.js';
import MyTischtennis from '../models/MyTischtennis.js';
import ClubTeam from '../models/ClubTeam.js';
import League from '../models/League.js';
import Season from '../models/Season.js';
import Member from '../models/Member.js';
import Match from '../models/Match.js';
import Team from '../models/Team.js';
import { Op } from 'sequelize';
import { devLog } from '../utils/logger.js';
import SeasonService from './seasonService.js';

class AutoFetchMatchResultsService {
  parseMatchDateTime(rawValue) {
    if (!rawValue) {
      return { date: null, time: null };
    }

    if (typeof rawValue === 'string') {
      const normalized = rawValue.trim();
      const hasExplicitTimezone = /(?:Z|[+-]\d{2}:?\d{2})$/i.test(normalized);

      // ISO values with explicit timezone are often UTC timestamps.
      // Convert them to local clock time before persisting date/time parts.
      if (hasExplicitTimezone) {
        const zoned = new Date(normalized);
        if (!Number.isNaN(zoned.getTime())) {
          return {
            date: new Date(zoned.getFullYear(), zoned.getMonth(), zoned.getDate(), 0, 0, 0, 0),
            time: `${String(zoned.getHours()).padStart(2, '0')}:${String(zoned.getMinutes()).padStart(2, '0')}:${String(zoned.getSeconds()).padStart(2, '0')}`
          };
        }
      }

      const match = normalized.match(/^(\d{4})-(\d{2})-(\d{2})(?:[T\s](\d{2}):(\d{2})(?::(\d{2}))?)?/);
      if (match) {
        const [, year, month, day, hour = '00', minute = '00', second = '00'] = match;
        return {
          date: new Date(Number(year), Number(month) - 1, Number(day), 0, 0, 0, 0),
          time: `${hour}:${minute}:${second}`
        };
      }
    }

    const parsed = new Date(rawValue);
    if (Number.isNaN(parsed.getTime())) {
      return { date: null, time: null };
    }

    return {
      date: new Date(parsed.getFullYear(), parsed.getMonth(), parsed.getDate(), 0, 0, 0, 0),
      time: `${String(parsed.getHours()).padStart(2, '0')}:${String(parsed.getMinutes()).padStart(2, '0')}:${String(parsed.getSeconds()).padStart(2, '0')}`
    };
  }

  /**
   * Execute automatic match results fetching for all users with enabled auto-updates
   */
  async executeAutomaticFetch(options = {}) {
    devLog('Starting automatic match results fetch...');
    
    try {
      const {
        userId = null,
        clubTeamId = null,
        currentSeasonOnly = true
      } = options;

      const currentSeason = currentSeasonOnly
        ? await SeasonService.getOrCreateCurrentSeason()
        : null;

      // Find all users with auto-updates enabled
      const accounts = await MyTischtennis.findAll({
        where: {
          ...(userId ? { userId } : {}),
          autoUpdateRatings: true,
          savePassword: true
        }
        // No attributes restriction — all fields needed for session handling and re-login
      });

      devLog(`Found ${accounts.length} accounts with auto-updates enabled for match results`);

      if (accounts.length === 0) {
        devLog('No accounts found with auto-updates enabled');
        return;
      }

      // Process each account and collect summaries
      const summaries = [];
      for (const account of accounts) {
        const summary = await this.processAccount(account, {
          clubTeamId,
          currentSeasonId: currentSeason?.id ?? null
        });
        summaries.push({ userId: account.userId, ...summary });
      }

      devLog('Automatic match results fetch completed');
      // Return overall summary including per-account counts
      const totalFetched = summaries.reduce((acc, s) => acc + (s.fetchedCount || 0), 0);
      return {
        success: true,
        totalFetched,
        currentSeasonId: currentSeason?.id ?? null,
        summaries
      };
    } catch (error) {
      console.error('Error in automatic match results fetch:', error);
      throw error;
    }
  }

  /**
   * Process a single account for match results fetching
   */
  async processAccount(account, options = {}) {
    const startTime = Date.now();
    let success = false;
    let message = '';
    let errorDetails = null;
    let fetchedCount = 0;
    let teamSummaries = [];

    try {
      devLog(`Processing match results for account ${account.email} (User ID: ${account.userId})`);

      // Check if session is still valid
      if (!account.accessToken || !account.expiresAt || account.expiresAt < Date.now() / 1000) {
        devLog(`Session expired for ${account.email}, attempting re-login via verifyLogin (incl. Playwright fallback)`);
        // verifyLogin handles CAPTCHA via Playwright and persists the session to DB.
        await myTischtennisService.verifyLogin(account.userId);
        // Reload the account to get the fresh session data written by verifyLogin.
        const refreshed = await MyTischtennis.findOne({ where: { userId: account.userId } });
        if (!refreshed?.cookie) {
          throw new Error('Re-login via verifyLogin did not produce a valid session');
        }
        account.accessToken = refreshed.accessToken;
        account.refreshToken = refreshed.refreshToken;
        account.expiresAt = refreshed.expiresAt;
        account.cookie = refreshed.cookie;
        devLog(`Successfully re-logged in for ${account.email}`);
      }

  // Perform match results fetch
  const fetchResult = await this.fetchMatchResults(account, options);
  fetchedCount = fetchResult.fetchedCount || 0;
  teamSummaries = fetchResult.teamSummaries || [];
      
  success = true;
  message = `Successfully fetched ${fetchedCount} match results`;
  devLog(`Fetched ${fetchedCount} match results for ${account.email}`);

    } catch (error) {
      success = false;
      message = 'Match results fetch failed';
      errorDetails = error.message;
      console.error(`Error fetching match results for ${account.email}:`, error);
    }

    const executionTime = Date.now() - startTime;

    // Log the attempt to MyTischtennisFetchLog
    await myTischtennisFetchLogService.logFetch(
      account.userId,
      'match_results',
      success,
      message,
      {
        errorDetails,
        recordsProcessed: fetchedCount,
        executionTime,
        isAutomatic: true
      }
    );
    
  devLog(`Match results fetch for ${account.email}: ${success ? 'SUCCESS' : 'FAILED'} (${executionTime}ms)`);

  // Return a summary for scheduler
  return { success, message, fetchedCount, errorDetails, executionTime, teamSummaries };
  }

  /**
   * Fetch match results for a specific account
   */
  async fetchMatchResults(account, options = {}) {
    devLog(`Fetching match results for ${account.email}`);
    
    let totalFetched = 0;
    
    try {
      const { clubTeamId = null, currentSeasonId = null } = options;

      // Get all teams for this user's clubs that have myTischtennis IDs configured
      const teams = await ClubTeam.findAll({
        where: {
          ...(clubTeamId ? { id: clubTeamId } : {}),
          ...(currentSeasonId ? { seasonId: currentSeasonId } : {}),
          myTischtennisTeamId: {
            [Op.ne]: null
          }
        },
        include: [
          {
            model: League,
            as: 'league',
            where: {
              myTischtennisGroupId: {
                [Op.ne]: null
              },
              association: {
                [Op.ne]: null
              }
            },
            include: [
              {
                model: Season,
                as: 'season'
              }
            ]
          }
        ]
      });

      devLog(`Found ${teams.length} teams with myTischtennis configuration`);

      const teamSummaries = [];

      // Fetch results for each team
      for (const team of teams) {
        try {
          const result = await this.fetchTeamResults(account, team);
          totalFetched += result.fetchedCount;
          teamSummaries.push({
            teamId: team.id,
            teamName: team.name,
            leagueId: team.leagueId,
            ...result.debugSummary
          });
        } catch (error) {
          console.error(`Error fetching results for team ${team.name}:`, error);
          teamSummaries.push({
            teamId: team.id,
            teamName: team.name,
            leagueId: team.leagueId,
            error: error.message
          });
        }
      }

      return {
        success: true,
        fetchedCount: totalFetched,
        teamSummaries
      };
    } catch (error) {
      console.error('Error in fetchMatchResults:', error);
      throw error;
    }
  }

  /**
   * Fetch results for a specific team
   */
  async fetchTeamResults(account, team) {
    // Validate required data
    if (!team.league) {
      throw new Error('Team league is required');
    }
    if (!team.league.season || !team.league.season.season) {
      throw new Error('Team league season is required');
    }
    
    const league = team.league;
    const season = league.season;

    // Build the myTischtennis URL
    // Convert full season (e.g. "2025/2026") to short format (e.g. "25/26") for API
    const seasonFull = season.season; // e.g. "2025/2026"
    const seasonParts = seasonFull.split('/');
    const seasonShort = seasonParts.length === 2 
      ? `${seasonParts[0].slice(-2)}/${seasonParts[1].slice(-2)}` 
      : seasonFull;
    const seasonStr = seasonShort.replace('/', '--'); // e.g. "25/26" -> "25--26"
    const teamnameEncoded = encodeURIComponent(team.name.replace(/\s/g, '_'));
    
    devLog(`=== FETCH TEAM RESULTS ===`);
    devLog(`Team name (from ClubTeam): ${team.name}`);
    devLog(`Team name encoded: ${teamnameEncoded}`);
    devLog(`MyTischtennis Team ID: ${team.myTischtennisTeamId}`);
    
    let totalProcessed = 0;
    const debugSummary = {
      scheduleMatchesProcessed: 0,
      leagueScheduleMatchesProcessed: 0,
      tableMatchesProcessed: 0,
      playerStatsProcessed: 0
    };

    try {
      const scheduleProcessed = await this.fetchTeamSchedule(account, team, seasonStr, teamnameEncoded);
      totalProcessed += scheduleProcessed;
      debugSummary.scheduleMatchesProcessed = scheduleProcessed;

      const leagueScheduleProcessed = await this.fetchLeagueSchedule(account, team, seasonStr);
      totalProcessed += leagueScheduleProcessed;
      debugSummary.leagueScheduleMatchesProcessed = leagueScheduleProcessed;

      // 1. Fetch player statistics (Spielerbilanzen)
      const playerStatsUrl = `https://www.mytischtennis.de/click-tt/${league.association}/${seasonStr}/ligen/${league.groupname}/gruppe/${league.myTischtennisGroupId}/mannschaft/${team.myTischtennisTeamId}/${teamnameEncoded}/spielerbilanzen/gesamt?_data=routes%2Fclick-tt%2B%2F%24association%2B%2F%24season%2B%2F%24type%2B%2F%28%24groupname%29.gruppe.%24urlid_.mannschaft.%24teamid.%24teamname%2B%2Fspielerbilanzen.%24filter`;

      devLog(`Fetching player stats from: ${playerStatsUrl}`);
      
      const fetchStartTime = Date.now();
      let playerStatsResponse = null;
      let fetchExecutionTime = 0;
      let responseStatus = 0;
      let responseBodyData = null;
      let fetchError = null;

      try {
        playerStatsResponse = await fetch(playerStatsUrl, {
          headers: {
            'Cookie': account.cookie || '',
            'Authorization': `Bearer ${account.accessToken}`,
            'Accept': 'application/json'
          }
        });
        
        fetchExecutionTime = Date.now() - fetchStartTime;
        responseStatus = playerStatsResponse.status;

          if (playerStatsResponse.ok) {
          const playerStatsData = await playerStatsResponse.json();
          responseBodyData = playerStatsData;
          
          // Avoid dumping full JSON to console; use devLog for compact info
          devLog(`Received player stats for team ${team.name} - balancesheet entries: ${Array.isArray(playerStatsData.data?.balancesheet) ? playerStatsData.data.balancesheet.length : 0}`);
          
          const playerCount = await this.processTeamData(team, playerStatsData);
          totalProcessed += playerCount;
          debugSummary.playerStatsProcessed = playerCount;
          devLog(`Processed ${playerCount} player statistics`);
        } else {
          // Read error response body
          try {
            responseBodyData = await playerStatsResponse.text();
          } catch (e) {
            responseBodyData = `Error reading response: ${e.message}`;
          }
        }
      } catch (error) {
        fetchExecutionTime = Date.now() - fetchStartTime;
        fetchError = error.message || String(error);
        responseStatus = 0;
        console.error('Error fetching from myTischtennis:', error);
      }
      
      // Log external request to myTischtennis - IMMER, auch bei Fehlern
      try {
        await apiLogService.logRequest({
          userId: account.userId,
          method: 'GET',
          path: playerStatsUrl.replace('https://www.mytischtennis.de', ''), // Relative path for consistency
          statusCode: responseStatus || (fetchError ? 0 : 200),
          requestBody: JSON.stringify({
            url: playerStatsUrl,
            myTischtennisTeamId: team.myTischtennisTeamId,
            clubTeamId: team.id,
            teamName: team.name,
            association: league.association,
            groupname: league.groupname,
            groupId: league.myTischtennisGroupId
          }),
          responseBody: responseBodyData ? (typeof responseBodyData === 'string' ? responseBodyData.substring(0, 10000) : JSON.stringify(responseBodyData).substring(0, 10000)) : null,
          executionTime: fetchExecutionTime,
          errorMessage: fetchError || (playerStatsResponse && !playerStatsResponse.ok ? `myTischtennis API returned ${responseStatus}` : null),
          logType: 'api_request',
          schedulerJobType: 'mytischtennis_fetch'
        });
      } catch (logError) {
        console.error('Error logging myTischtennis request:', logError);
        // Don't throw - logging failures shouldn't break the main operation
      }
      
      // Re-throw error if fetch failed
      if (fetchError) {
        throw new Error(`Failed to fetch from myTischtennis: ${fetchError}`);
      }

      // Note: Match results are already included in the player stats response above
      // in tableData.meetings_excerpt.meetings, so we don't need a separate call

      // Also fetch and update league table data for this team
      if (account.userId) {
        try {
          const tableStartTime = Date.now();
          const tableResult = await this.fetchAndUpdateLeagueTable(account.userId, team.leagueId);
          const tableExecutionTime = Date.now() - tableStartTime;
          totalProcessed += tableResult?.matchesProcessed || 0;
          debugSummary.tableMatchesProcessed = tableResult?.matchesProcessed || 0;
          
          devLog(`✓ League table updated for league ${team.leagueId}`);
          
          // Log league table fetch
          await myTischtennisFetchLogService.logFetch(
            account.userId,
            'league_table',
            true,
            `League table updated successfully`,
            {
              recordsProcessed: tableResult?.teamsUpdated || 0,
              executionTime: tableExecutionTime,
              isAutomatic: true,
              leagueId: team.leagueId
            }
          );
        } catch (error) {
          console.error(`Error updating league table for league ${team.leagueId}:`, error);
          
          // Log failed table fetch
          await myTischtennisFetchLogService.logFetch(
            account.userId,
            'league_table',
            false,
            'League table update failed',
            {
              errorDetails: error.message,
              isAutomatic: true,
              leagueId: team.leagueId
            }
          );
        }
      } else {
        devLog(`Skipping league table update - no userId available`);
      }

      return {
        success: true,
        fetchedCount: totalProcessed,
        debugSummary
      };
    } catch (error) {
      console.error(`Error fetching team results for ${team.name}:`, error);
      throw error;
    }
  }

  async fetchTeamSchedule(account, team, seasonStr, teamnameEncoded) {
    const league = team.league;
    const schedulePath = `/click-tt/${league.association}/${seasonStr}/ligen/${league.groupname}/gruppe/${league.myTischtennisGroupId}/mannschaft/${team.myTischtennisTeamId}/${teamnameEncoded}/spielplan`;
    const scheduleDataRoutes = [
      `${schedulePath}?_data=routes%2Fclick-tt%2B%2F%24association%2B%2F%24season%2B%2F%24type%2B%2F%28%24groupname%29.gruppe.%24urlid_.mannschaft.%24teamid.%24teamname%2B%2Fspielplan`,
      `${schedulePath}?_data=routes%2Fclick-tt%2B%2F%24association%2B%2F%24season%2B%2F%24type%2B%2F%28%24groupname%29.gruppe.%24urlid_.mannschaft.%24teamid.%24teamname%2B%2Fspielplan.%24filter`,
      schedulePath
    ];

    for (const endpoint of scheduleDataRoutes) {
      try {
        devLog(`Fetching schedule from: ${endpoint}`);
        const response = await myTischtennisClient.authenticatedRequest(endpoint, account.cookie || '', {
          method: 'GET'
        });

        if (!response.success) {
          devLog(`Schedule fetch failed for endpoint ${endpoint}: ${response.error}`);
          continue;
        }

        const payload = response.data;
        if (!payload) {
          continue;
        }

        const processed = await this.processMatchResults(team, payload);
        if (processed > 0) {
          devLog(`Processed ${processed} matches from team schedule for ${team.name}`);
          return processed;
        }
      } catch (error) {
        console.error(`Error fetching team schedule for ${team.name}:`, error);
      }
    }

    return 0;
  }

  buildLeagueScheduleDataRoute(roundKey) {
    return 'routes%2Fclick-tt%2B%2F%24association%2B%2F%24season%2B%2F%24type%2B%2F%24groupname.gruppe.%24urlid%2B%2Fspielplan.%24filter';
  }

  buildLeagueGroupNameVariants(groupname) {
    const variants = [];
    if (typeof groupname === 'string' && groupname.trim()) {
      variants.push(groupname);
      variants.push(groupname.replace(/\s/g, '_'));
      variants.push(`_${groupname.replace(/\s/g, '_')}`);
    }
    return [...new Set(variants)];
  }

  async fetchLeagueSchedule(account, team, seasonStr) {
    const league = team.league;
    const groupNameVariants = this.buildLeagueGroupNameVariants(league.groupname);
    const roundKeys = ['vr', 'rr'];
    let totalProcessed = 0;

    for (const groupName of groupNameVariants) {
      for (const roundKey of roundKeys) {
        const endpoint = `/click-tt/${league.association}/${seasonStr}/ligen/${groupName}/gruppe/${league.myTischtennisGroupId}/spielplan/${roundKey}?_data=${this.buildLeagueScheduleDataRoute(roundKey)}`;

        try {
          devLog(`Fetching league schedule from: ${endpoint}`);
          const response = await myTischtennisClient.authenticatedRequest(endpoint, account.cookie || '', {
            method: 'GET'
          });

          if (!response.success) {
            devLog(`League schedule fetch failed for endpoint ${endpoint}: ${response.error}`);
            continue;
          }

          const processed = await this.processMatchResults(team, response.data);
          if (processed > 0) {
            devLog(`Processed ${processed} matches from league ${roundKey.toUpperCase()} schedule for ${team.name}`);
            totalProcessed += processed;
          }
        } catch (error) {
          console.error(`Error fetching league ${roundKey.toUpperCase()} schedule for ${team.name}:`, error);
        }
      }

      if (totalProcessed > 0) {
        return totalProcessed;
      }
    }

    return 0;
  }

  /**
   * Process and store team data from myTischtennis
   */
  async processTeamData(team, data) {
    devLog(`Processing data for team ${team.name}`);
    
    if (!data.data || !data.data.balancesheet || !Array.isArray(data.data.balancesheet)) {
      devLog('No balancesheet data found');
      return 0;
    }

    let processedCount = 0;

    for (const teamData of data.data.balancesheet) {
      processedCount += await this.processSinglePlayerStatistics(teamData);
      processedCount += await this.processDoublePlayerStatistics(teamData);
    }

    // Also process meetings from the player stats response
    if (data.data && data.data.balancesheet && data.data.balancesheet[0]) {
      const teamData = data.data.balancesheet[0];
      
      // Check for meetings_excerpt in the tableData section
      if (data.tableData && data.tableData.meetings_excerpt && data.tableData.meetings_excerpt.meetings) {
        devLog('Found meetings_excerpt in tableData, processing matches...');
        const meetingsProcessed = await this.processMatchResults(team, { data: data.tableData });
        devLog(`Processed ${meetingsProcessed} matches from player stats response`);
      }
    }

    return processedCount;
  }

  async processSinglePlayerStatistics(teamData) {
    if (!Array.isArray(teamData.single_player_statistics)) {
      return 0;
    }

    let processedCount = 0;

    for (const playerStat of teamData.single_player_statistics) {
      devLog(`Player: ${playerStat.player_firstname} ${playerStat.player_lastname} (ID: ${playerStat.player_id})`);
      devLog(`  Points won: ${playerStat.points_won}, Points lost: ${playerStat.points_lost}`);

      const member = await this.matchPlayer(
        playerStat.player_id,
        playerStat.player_firstname,
        playerStat.player_lastname
      );

      if (member) {
        devLog(`  Matched with local member: ${member.firstName} ${member.lastName} (ID: ${member.id})`);
        await this.ensureMemberPlayerId(member, playerStat.player_id);
      } else {
        devLog(`  No local member found for ${playerStat.player_firstname} ${playerStat.player_lastname}`);
      }

      processedCount++;
    }

    return processedCount;
  }

  async processDoublePlayerStatistics(teamData) {
    if (!Array.isArray(teamData.double_player_statistics)) {
      return 0;
    }

    let processedCount = 0;

    for (const doubleStat of teamData.double_player_statistics) {
      const firstPlayerLabel = `${doubleStat.firstname_player_1} ${doubleStat.lastname_player_1}`.trim();
      const secondPlayerLabel = `${doubleStat.firstname_player_2} ${doubleStat.lastname_player_2}`.trim();

      devLog(`Double: ${firstPlayerLabel} / ${secondPlayerLabel}`);
      devLog(`  Points won: ${doubleStat.points_won}, Points lost: ${doubleStat.points_lost}`);

      const [memberOne, memberTwo] = await Promise.all([
        this.matchPlayer(
          doubleStat.player_id_1 || doubleStat.player_1_id || null,
          doubleStat.firstname_player_1,
          doubleStat.lastname_player_1
        ),
        this.matchPlayer(
          doubleStat.player_id_2 || doubleStat.player_2_id || null,
          doubleStat.firstname_player_2,
          doubleStat.lastname_player_2
        )
      ]);

      if (memberOne) {
        await this.ensureMemberPlayerId(memberOne, doubleStat.player_id_1 || doubleStat.player_1_id || null);
      } else {
        devLog(`  No local member found for double player 1: ${firstPlayerLabel}`);
      }

      if (memberTwo) {
        await this.ensureMemberPlayerId(memberTwo, doubleStat.player_id_2 || doubleStat.player_2_id || null);
      } else {
        devLog(`  No local member found for double player 2: ${secondPlayerLabel}`);
      }

      if (memberOne && memberTwo) {
        devLog(`  Matched double with local members: ${memberOne.firstName} ${memberOne.lastName} / ${memberTwo.firstName} ${memberTwo.lastName}`);
      }

      processedCount++;
    }

    return processedCount;
  }

  async ensureMemberPlayerId(member, playerId) {
    if (!playerId || member.myTischtennisPlayerId) {
      return;
    }

    member.myTischtennisPlayerId = playerId;
    await member.save();
    devLog(`  Updated myTischtennis Player ID for ${member.firstName} ${member.lastName}`);
  }

  /**
   * Process match results from schedule/table data
   */
  async processMatchResults(team, data) {
    devLog(`Processing match results for team ${team.name}`);
    
    // Handle different response structures from different endpoints
    const meetingSource =
      data.data?.meetings_excerpt ||
      data.tableData?.meetings_excerpt ||
      data.meetings_excerpt ||
      data.data?.meetings ||
      data.meetings;

    if (!meetingSource) {
      devLog('No meeting data found in response');
      return 0;
    }

    const meetings = meetingSource.meetings || meetingSource;
    
    if (!Array.isArray(meetings) || meetings.length === 0) {
      devLog('No meetings array found or empty');
      return 0;
    }
    
    devLog(`Found ${meetings.length} items in meetings array`);

    const firstItem = meetings[0];
    const isDateGrouped = firstItem && typeof firstItem === 'object' && !firstItem.meeting_id;
    const flattenedMatches = [];

    if (isDateGrouped) {
      devLog('Processing date-grouped meetings...');
      for (const dateGroup of meetings) {
        for (const matchList of Object.values(dateGroup)) {
          if (Array.isArray(matchList)) {
            flattenedMatches.push(...matchList);
          }
        }
      }
    } else {
      devLog('Processing flat meetings array...');
      flattenedMatches.push(...meetings);
    }

    const uniqueMatches = flattenedMatches.filter((match, index, list) => {
      if (!match?.meeting_id) {
        return true;
      }
      return list.findIndex((entry) => entry?.meeting_id === match.meeting_id) === index;
    });

    let processedCount = 0;
    for (const match of uniqueMatches) {
      devLog(`Match: ${match.team_home} vs ${match.team_away}`);
      devLog(`  Date: ${match.date}`);
      devLog(`  Status: ${match.state} (${match.is_meeting_complete ? 'complete' : 'incomplete'})`);
      devLog(`  Result: ${match.matches_won}:${match.matches_lost}`);
      devLog(`  Meeting ID: ${match.meeting_id}`);

      try {
        await this.storeMatchResult(team, match, false);
        processedCount++;
      } catch (error) {
        console.error(`Error storing match result for meeting ${match.meeting_id}:`, error);
      }
    }

    devLog(`Processed ${processedCount} matches in league ${team.leagueId}`);
    return processedCount;
  }

  /**
   * Store or update match result in database
   */
  async storeMatchResult(ourClubTeam, matchData, isHomeTeam) {
    const parsedMatchDateTime = this.parseMatchDateTime(matchData.date);

    // Parse match points from myTischtennis data
    // matchData.matches_won/lost are ALWAYS from the perspective of team_home in myTischtennis
    // So we need to assign them correctly based on whether WE are home or guest
    const mtHomePoints = parseInt(matchData.matches_won) || 0;
    const mtGuestPoints = parseInt(matchData.matches_lost) || 0;
    
    // If matchData has team_home and team_away, we can determine our role
    // But isHomeTeam parameter tells us if WE (ourClubTeam) are playing at home
    const homeMatchPoints = mtHomePoints;
    const guestMatchPoints = mtGuestPoints;
    
    devLog(`Match points from myTischtennis: ${mtHomePoints}:${mtGuestPoints} (from team_home perspective)`);

    // Find existing match by meeting ID OR by date and team names
    devLog(`Searching for existing match with meeting ID: ${matchData.meeting_id}`);
    let match = await Match.findOne({
      where: { myTischtennisMeetingId: matchData.meeting_id }
    });

    if (match) {
      devLog(`Found match by meeting ID: ${match.id}`);
    }

    // If not found by meeting ID, try to find by date and teams
    if (!match) {
      devLog(`No match found by meeting ID, searching by date and teams...`);
      const matchDate = parsedMatchDateTime.date;
      if (!matchDate) {
        throw new Error(`Ungültiges Match-Datum von myTischtennis: ${matchData.date}`);
      }
      const startOfDay = new Date(matchDate.getFullYear(), matchDate.getMonth(), matchDate.getDate(), 0, 0, 0, 0);
      const endOfDay = new Date(matchDate.getFullYear(), matchDate.getMonth(), matchDate.getDate(), 23, 59, 59, 999);

      devLog(`Searching matches on ${matchData.date} in league ${ourClubTeam.leagueId}`);

      const potentialMatches = await Match.findAll({
        where: {
          date: {
            [Op.between]: [startOfDay, endOfDay]
          },
          leagueId: ourClubTeam.leagueId
        },
        include: [
          { model: Team, as: 'homeTeam' },
          { model: Team, as: 'guestTeam' }
        ]
      });

      devLog(`Found ${potentialMatches.length} potential matches on this date`);

      // Find by team names
      for (const m of potentialMatches) {
        devLog(`  Checking match ${m.id}: ${m.homeTeam?.name} vs ${m.guestTeam?.name}`);
        devLog(`    Against: ${matchData.team_home} vs ${matchData.team_away}`);
        
        const homeNameMatch = m.homeTeam?.name === matchData.team_home || 
                             m.homeTeam?.name.includes(matchData.team_home) ||
                             matchData.team_home.includes(m.homeTeam?.name);
        const guestNameMatch = m.guestTeam?.name === matchData.team_away ||
                              m.guestTeam?.name.includes(matchData.team_away) ||
                              matchData.team_away.includes(m.guestTeam?.name);
        
        devLog(`    Home match: ${homeNameMatch}, Guest match: ${guestNameMatch}`);
        
        if (homeNameMatch && guestNameMatch) {
          match = m;
          devLog(`  ✓ Found existing match by date and teams: ${match.id}`);
          break;
        }
      }

      if (!match) {
        devLog(`No existing match found, will create new one`);
      }
    }

    if (match) {
      // Update existing match
      // IMPORTANT: Check if the teams are in the same order as in myTischtennis
      // Load the match with team associations to compare
      const matchWithTeams = await Match.findByPk(match.id, {
        include: [
          { model: Team, as: 'homeTeam' },
          { model: Team, as: 'guestTeam' }
        ]
      });

      // Compare team names to determine if we need to swap points
      const dbHomeTeamName = matchWithTeams.homeTeam?.name || '';
      const dbGuestTeamName = matchWithTeams.guestTeam?.name || '';
      const mtHomeTeamName = matchData.team_home;
      const mtGuestTeamName = matchData.team_away;

      // Check if teams are in the same order
      const teamsMatch = (
        dbHomeTeamName === mtHomeTeamName || 
        dbHomeTeamName.includes(mtHomeTeamName) || 
        mtHomeTeamName.includes(dbHomeTeamName)
      );

      let finalHomePoints, finalGuestPoints;
      
      if (teamsMatch) {
        // Teams are in same order
        finalHomePoints = homeMatchPoints;
        finalGuestPoints = guestMatchPoints;
        devLog(`Teams in same order: ${dbHomeTeamName} = ${mtHomeTeamName}`);
      } else {
        // Teams are swapped - need to swap points!
        finalHomePoints = guestMatchPoints;
        finalGuestPoints = homeMatchPoints;
        devLog(`Teams are SWAPPED! DB: ${dbHomeTeamName} vs ${dbGuestTeamName}, MyTT: ${mtHomeTeamName} vs ${mtGuestTeamName}`);
        devLog(`Swapping points: ${homeMatchPoints}:${guestMatchPoints} → ${finalHomePoints}:${finalGuestPoints}`);
      }

      const updateData = {
        date: parsedMatchDateTime.date || match.date,
        time: parsedMatchDateTime.time || match.time,
        homeMatchPoints: finalHomePoints,
        guestMatchPoints: finalGuestPoints,
        isCompleted: matchData.is_meeting_complete,
        pdfUrl: matchData.pdf_url,
        myTischtennisMeetingId: matchData.meeting_id // Store meeting ID for future updates
      };

      await match.update(updateData);
      devLog(`Updated existing match ${match.id} (Meeting ${matchData.meeting_id}): ${finalHomePoints}:${finalGuestPoints} (${matchData.is_meeting_complete ? 'complete' : 'incomplete'})`);
    } else {
      // Create new match
      devLog(`Creating new match for meeting ${matchData.meeting_id}`);
      
      try {
        // Find or create home and guest teams based on myTischtennis team IDs
        const homeTeam = await this.findOrCreateTeam(
          matchData.team_home,
          matchData.team_home_id,
          ourClubTeam
        );
        
        const guestTeam = await this.findOrCreateTeam(
          matchData.team_away,
          matchData.team_away_id,
          ourClubTeam
        );

        // Extract time from date
        const matchDate = parsedMatchDateTime.date;
        const time = parsedMatchDateTime.time;
        if (!matchDate) {
          throw new Error(`Ungültiges Match-Datum von myTischtennis: ${matchData.date}`);
        }

        // Create match (points are already correctly set from matchData)
        match = await Match.create({
          date: matchDate,
          time: time,
          locationId: null, // Location is not provided by myTischtennis
          homeTeamId: homeTeam.id,
          guestTeamId: guestTeam.id,
          leagueId: ourClubTeam.leagueId,
          clubId: ourClubTeam.clubId,
          myTischtennisMeetingId: matchData.meeting_id,
          homeMatchPoints: homeMatchPoints,
          guestMatchPoints: guestMatchPoints,
          isCompleted: matchData.is_meeting_complete,
          pdfUrl: matchData.pdf_url
        });

        devLog(`Created new match ${match.id}: ${matchData.team_home} vs ${matchData.team_away} (${homeMatchPoints}:${guestMatchPoints}, ${matchData.is_meeting_complete ? 'complete' : 'incomplete'})`);
      } catch (error) {
        console.error(`Error creating match for meeting ${matchData.meeting_id}:`, error);
        devLog(`  Home: ${matchData.team_home} (myTT ID: ${matchData.team_home_id})`);
        devLog(`  Guest: ${matchData.team_away} (myTT ID: ${matchData.team_away_id})`);
      }
    }

    return match;
  }

  /**
   * Find or create a Team in the team table
   * All teams (own and opponents) are stored in the team table
   */
  async findOrCreateTeam(teamName, myTischtennisTeamId, ourClubTeam) {
    devLog(`Finding team: ${teamName} (myTT ID: ${myTischtennisTeamId})`);
    
    // Search in team table for all teams in this league
    const allTeamsInLeague = await Team.findAll({
      where: {
        leagueId: ourClubTeam.leagueId,
        seasonId: ourClubTeam.seasonId
      }
    });

    devLog(`  Searching in ${allTeamsInLeague.length} teams in league ${ourClubTeam.leagueId}`);

    // Try exact match first
    let team = allTeamsInLeague.find(t => t.name === teamName);
    
    if (team) {
      devLog(`  ✓ Found team by exact name: ${team.name} (ID: ${team.id})`);
      return team;
    }
    
    // If not found, try fuzzy match
    team = allTeamsInLeague.find(t => 
      t.name.includes(teamName) ||
      teamName.includes(t.name)
    );
    
    if (team) {
      devLog(`  ✓ Found team by fuzzy match: ${team.name} (ID: ${team.id})`);
      return team;
    }

    // Team not found - create it
    team = await Team.create({
      name: teamName,
      clubId: ourClubTeam.clubId,
      leagueId: ourClubTeam.leagueId,
      seasonId: ourClubTeam.seasonId
    });
    devLog(`  ✓ Created new team: ${team.name} (ID: ${team.id})`);

    return team;
  }

  /**
   * Match a myTischtennis player with a local Member
   */
  async matchPlayer(playerId, firstName, lastName) {
    // First, try to find by myTischtennis Player ID
    if (playerId) {
      const member = await Member.findOne({
        where: { myTischtennisPlayerId: playerId }
      });
      
      if (member) {
        return member;
      }
    }
    
    // If not found, try to match by name (fuzzy matching)
    // Note: Since names are encrypted, we need to get all members and decrypt
    // This is not efficient for large databases, but works for now
    const allMembers = await Member.findAll();
    
    for (const member of allMembers) {
      const memberFirstName = member.firstName?.toLowerCase().trim();
      const memberLastName = member.lastName?.toLowerCase().trim();
      const searchFirstName = firstName?.toLowerCase().trim();
      const searchLastName = lastName?.toLowerCase().trim();
      
      if (memberFirstName === searchFirstName && memberLastName === searchLastName) {
        return member;
      }
    }
    
    return null;
  }

  /**
   * Get all accounts with auto-fetch enabled (for manual execution)
   */
  async getAutoFetchAccounts() {
    return await MyTischtennis.findAll({
      where: {
        autoUpdateRatings: true
      },
      attributes: ['userId', 'email', 'autoUpdateRatings']
    });
  }

  /**
   * Fetch and update league table data from MyTischtennis
   * @param {number} userId - User ID
   * @param {number} leagueId - League ID
   */
  async fetchAndUpdateLeagueTable(userId, leagueId) {
    try {
      devLog(`Fetching league table for user ${userId}, league ${leagueId}`);
      
      // Get user's MyTischtennis account
      const myTischtennisAccount = await MyTischtennis.findOne({
        where: { userId }
      });
      
      if (!myTischtennisAccount) {
        throw new Error('MyTischtennis account not found');
      }

      // Get league info
      const league = await League.findByPk(leagueId, {
        include: [{ model: Season, as: 'season' }]
      });
      
      if (!league) {
        throw new Error('League not found');
      }

      // Login to MyTischtennis if needed
      let session = await myTischtennisService.getSession(userId);
      if (!session || !session.isValid) {
        if (!myTischtennisAccount.savePassword) {
          throw new Error('MyTischtennis account not connected or session expired');
        }
        
        devLog('Session expired, re-logging in...');
        await myTischtennisService.verifyLogin(userId);
        session = await myTischtennisService.getSession(userId);
      }

      // Convert full season (e.g. "2025/2026") to short format (e.g. "25/26") and then to URL format (e.g. "25--26")
      const seasonFull = league.season.season; // e.g. "2025/2026"
      const seasonParts = seasonFull.split('/');
      const seasonShort = seasonParts.length === 2 
        ? `${seasonParts[0].slice(-2)}/${seasonParts[1].slice(-2)}` 
        : seasonFull;
      const seasonStr = seasonShort.replace('/', '--'); // e.g. "25/26" -> "25--26"
      
      // Fetch table data from MyTischtennis
      const tableUrl = `https://www.mytischtennis.de/click-tt/${league.association}/${seasonStr}/ligen/${league.groupname}/gruppe/${league.myTischtennisGroupId}/tabelle/gesamt?_data=routes%2Fclick-tt%2B%2F%24association%2B%2F%24season%2B%2F%24type%2B%2F%24groupname.gruppe.%24urlid%2B%2Ftabelle.%24filter`;
      
  devLog(`[fetchAndUpdateLeagueTable] Fetching table from URL: ${tableUrl}`);
      const response = await myTischtennisClient.authenticatedRequest(tableUrl, session.cookie, {
        method: 'GET'
      });
      
      if (!response.success) {
        throw new Error(`Failed to fetch table data: ${response.error}`);
      }

      const tableData = await this.parseTableData(JSON.stringify(response.data), leagueId);
      
      // Update teams with table data
      const teamsUpdated = await this.updateTeamsWithTableData(tableData, leagueId);
      
      devLog(`✓ Updated league table for league ${leagueId} with ${tableData.length} teams`);
      
      return {
        teamsUpdated,
        totalTeams: tableData.length,
        matchesProcessed: 0
      };
      
    } catch (error) {
      console.error(`Error fetching league table for league ${leagueId}:`, error);
      throw error;
    }
  }

  /**
   * Parse table data from MyTischtennis response
   * @param {string} jsonResponse - JSON response from MyTischtennis
   * @param {number} leagueId - League ID
   * @returns {Array} Parsed table data
   */
  async parseTableData(jsonResponse, leagueId) {
    devLog('Parsing table data from MyTischtennis response...');
    
    try {
      const data = JSON.parse(jsonResponse);
      
      if (!data.data || !data.data.league_table) {
        devLog('No league table data found in response');
        return [];
      }
      
      const leagueTable = data.data.league_table;
      const parsedData = [];
      
      for (const teamData of leagueTable) {
        parsedData.push({
          teamName: teamData.team_name,
          matchesPlayed: teamData.matches_won + teamData.matches_lost,
          matchesWon: teamData.matches_won,
          matchesLost: teamData.matches_lost,
          matchesTied: teamData.meetings_tie || 0, // Unentschiedene Begegnungen
          setsWon: teamData.sets_won,
          setsLost: teamData.sets_lost,
          pointsWon: teamData.games_won, // MyTischtennis uses "games" for Ballpunkte
          pointsLost: teamData.games_lost,
          tablePointsWon: teamData.points_won, // Liga-Tabellenpunkte gewonnen
          tablePointsLost: teamData.points_lost, // Liga-Tabellenpunkte verloren
          tableRank: teamData.table_rank
        });
      }
      
      devLog(`Parsed ${parsedData.length} teams from MyTischtennis table data`);
      return parsedData;
      
    } catch (error) {
      console.error('Error parsing MyTischtennis table data:', error);
      return [];
    }
  }

  /**
   * Update teams with table data
   * @param {Array} tableData - Parsed table data
   * @param {number} leagueId - League ID
   */
  async updateTeamsWithTableData(tableData, leagueId) {
    let updatedCount = 0;
    
    for (const teamData of tableData) {
      try {
        // Find team by name in this league
        const team = await Team.findOne({
          where: {
            leagueId: leagueId,
            name: { [Op.like]: `%${teamData.teamName}%` }
          }
        });

        if (team) {
          await team.update({
            matchesPlayed: teamData.matchesPlayed || 0,
            matchesWon: teamData.matchesWon || 0,
            matchesLost: teamData.matchesLost || 0,
            matchesTied: teamData.matchesTied || 0,
            setsWon: teamData.setsWon || 0,
            setsLost: teamData.setsLost || 0,
            pointsWon: teamData.pointsWon || 0,
            pointsLost: teamData.pointsLost || 0,
            tablePoints: (teamData.tablePointsWon || 0), // Legacy field (keep for compatibility)
            tablePointsWon: teamData.tablePointsWon || 0,
            tablePointsLost: teamData.tablePointsLost || 0
          });
          
          updatedCount++;
          devLog(`  ✓ Updated team ${team.name} with table data`);
        } else {
          devLog(`  ⚠ Team not found: ${teamData.teamName}`);
        }
      } catch (error) {
        console.error(`Error updating team ${teamData.teamName}:`, error);
      }
    }
    
    return updatedCount;
  }
}

export default new AutoFetchMatchResultsService();
