// services/playerService.js

const axios = require("axios");
const Player = require("../models/Player");
const Match = require("../models/Match");

// Base URL for the V1 API provided by TheSportsDB
const baseURL = "https://www.thesportsdb.com/api/v1/json/3";

// Helper function to make API requests
const makeApiRequest = async (endpoint, params = {}) => {
  try {
    const response = await axios.get(`${baseURL}/${endpoint}`, { params });
    console.log(
      `API Response for ${endpoint} with params ${JSON.stringify(params)}:`,
      response.data
    );
    return response.data;
  } catch (error) {
    if (error.response) {
      console.error(
        `API Error: ${error.response.status} - ${JSON.stringify(
          error.response.data
        )}`
      );
      throw new Error(
        `API Error: ${error.response.data.error || "Unknown error"}`
      );
    } else if (error.request) {
      console.error("No response received from API");
      throw new Error("No response received from API");
    } else {
      console.error("Error setting up the request:", error.message);
      throw new Error("Error setting up the request");
    }
  }
};

/**
 * Search for players by name.
 * Returns up to 5 matching players.
 */
const searchPlayers = async (query) => {
  if (!query) {
    throw new Error("Search query is required");
  }

  // Search in the local database first
  const localPlayers = await Player.find({
    name: { $regex: query, $options: "i" },
  }).limit(5);

  if (localPlayers.length > 0) {
    return { count: localPlayers.length, players: localPlayers };
  }

  // If not found locally, fetch from API
  const playersResponse = await makeApiRequest("searchplayers.php", {
    p: query,
  });

  // Validate response structure
  if (
    !playersResponse ||
    !playersResponse.player ||
    !Array.isArray(playersResponse.player)
  ) {
    throw new Error("No players found matching the query");
  }

  // Limit to 5 players
  const limitedPlayers = playersResponse.player.slice(0, 5);

  // Enrich player data with necessary fields
  const enrichedPlayers = limitedPlayers.map((player) => ({
    player_id: player.idPlayer,
    name: player.strPlayer,
    position: player.strPosition || "N/A",
    nationality: player.strNationality || "N/A",
    team: player.strTeam || "N/A",
    league: player.strLeague || "N/A",
    photo_url: player.strThumb || null,
    description: player.strDescriptionEN || "No description available.",
    birthday: player.dateBorn || "N/A",
    height: player.strHeight || "N/A",
    weight: player.strWeight || "N/A",
    jerseyNumber: player.strNumber || "N/A",
    social: {
      twitter: player.strTwitter || null,
      facebook: player.strFacebook || null,
      instagram: player.strInstagram || null,
    },
  }));

  // Save fetched players to the local database
  await Player.insertMany(enrichedPlayers, { ordered: false }).catch((err) => {
    if (err.code === 11000) {
      console.warn("Duplicate player entries detected.");
    } else {
      console.error("Error inserting players into DB:", err.message);
    }
  });

  return { count: enrichedPlayers.length, players: enrichedPlayers };
};

/**
 * Get detailed information about a specific player by ID.
 */
const getPlayerDetails = async (playerId) => {
  if (!playerId) {
    throw new Error("Player ID is required");
  }

  // Check local DB first
  const localPlayer = await Player.findOne({ player_id: playerId });

  if (localPlayer) {
    console.log(`Player ID "${playerId}" found in local DB.`);
    return { status: "success", response: localPlayer };
  }

  console.log(
    `Player ID "${playerId}" not found in local DB. Fetching from API.`
  );

  // If not found locally, fetch from API
  const playerDetailsResponse = await makeApiRequest("lookupplayer.php", {
    id: playerId,
  });

  // Validate response structure
  if (
    !playerDetailsResponse ||
    playerDetailsResponse.players === null ||
    !Array.isArray(playerDetailsResponse.players) ||
    playerDetailsResponse.players.length === 0
  ) {
    console.warn(`API returned no data for Player ID "${playerId}".`);
    throw new Error("Player not found");
  }

  const player = playerDetailsResponse.players[0];

  // Structure the player details
  const detailedPlayer = {
    player_id: player.idPlayer,
    name: player.strPlayer,
    position: player.strPosition || "N/A",
    nationality: player.strNationality || "N/A",
    team: player.strTeam || "N/A",
    league: player.strLeague || "N/A",
    photo_url: player.strThumb || null,
    description: player.strDescriptionEN || "No description available.",
    birthday: player.dateBorn || "N/A",
    height: player.strHeight || "N/A",
    weight: player.strWeight || "N/A",
    jerseyNumber: player.strNumber || "N/A",
    social: {
      twitter: player.strTwitter || null,
      facebook: player.strFacebook || null,
      instagram: player.strInstagram || null,
    },
  };

  console.log(`Fetched player details for Player ID "${playerId}" from API.`);

  // Save to local DB
  try {
    await Player.create(detailedPlayer);
    console.log(`Player ID "${playerId}" saved to local DB.`);
  } catch (err) {
    if (err.code === 11000) {
      console.warn(`Player ID "${playerId}" already exists in the database.`);
    } else {
      console.error("Error inserting player into DB:", err.message);
    }
  }

  return { status: "success", response: detailedPlayer };
};

/**
 * Get recent matches/events for a specific player.
 * Note: This is a simplified version. Adjust based on your actual requirements.
 */
const getPlayerMatches = async (playerId, limit = 5) => {
  if (!playerId) {
    throw new Error("Player ID is required");
  }

  // Check if the player exists in the database
  const player = await Player.findOne({ player_id: playerId });
  if (!player) {
    console.log(
      `Player ID "${playerId}" not found in local DB. Fetching player details to proceed.`
    );
    // Attempt to fetch player details from API
    await getPlayerDetails(playerId);
  } else {
    console.log(`Player ID "${playerId}" found in local DB.`);
  }

  // Now that the player exists in the database, fetch matches
  // Check local DB for matches
  const localMatches = await Match.find({ player_id: playerId })
    .sort({ date: -1 })
    .limit(limit);

  if (localMatches.length >= limit) {
    console.log(
      `Found ${localMatches.length} matches in local DB for Player ID "${playerId}".`
    );
    return { count: localMatches.length, matches: localMatches };
  }

  console.log(
    `Found only ${localMatches.length} matches in local DB for Player ID "${playerId}". Fetching additional matches from API.`
  );

  // Fetch player details to get the team ID
  const playerDetailsResponse = await makeApiRequest("lookupplayer.php", {
    id: playerId,
  });

  if (
    !playerDetailsResponse ||
    playerDetailsResponse.players === null ||
    !Array.isArray(playerDetailsResponse.players) ||
    playerDetailsResponse.players.length === 0
  ) {
    throw new Error("Player not found");
  }

  const playerData = playerDetailsResponse.players[0];
  const teamId = playerData.idTeam;

  if (!teamId) {
    throw new Error("Team ID not available for this player");
  }

  // Fetch recent events for the team
  const eventsResponse = await makeApiRequest("eventslast.php", {
    id: teamId,
  });

  if (
    !eventsResponse ||
    !eventsResponse.results ||
    !Array.isArray(eventsResponse.results)
  ) {
    throw new Error("No events found for the player's team");
  }

  // Structure the match details
  const matches = eventsResponse.results
    .slice(0, limit - localMatches.length)
    .map((event) => ({
      event_id: event.idEvent,
      league: event.strLeague,
      season: event.strSeason,
      date: event.dateEvent,
      time: event.strTime,
      homeTeam: event.strHomeTeam,
      awayTeam: event.strAwayTeam,
      homeScore: event.intHomeScore,
      awayScore: event.intAwayScore,
      status: event.strStatus,
      player_id: playerId,
      fetchedAt: new Date(),
    }));

  console.log(
    `Fetched ${matches.length} new matches from API for Player ID "${playerId}".`
  );

  // Save new matches to local DB
  await Match.insertMany(matches, { ordered: false }).catch((err) => {
    if (err.code === 11000) {
      console.warn("Duplicate match entries detected.");
    } else {
      console.error("Error inserting matches into DB:", err.message);
    }
  });

  // Combine local and newly fetched matches
  const combinedMatches = [...localMatches, ...matches].slice(0, limit);

  console.log(
    `Returning ${combinedMatches.length} matches for Player ID "${playerId}".`
  );

  return { count: combinedMatches.length, matches: combinedMatches };
};

module.exports = {
  searchPlayers,
  getPlayerDetails,
  getPlayerMatches,
};
// controllers/playerSearchController.js

const playerService = require("../services/playerService");

/**
 * Search for players by name.
 * Route: GET /players/search?q=playerName
 */
exports.searchPlayers = async (req, res) => {
  const query = req.query.q;

  try {
    const result = await playerService.searchPlayers(query);
    res.json(result);
  } catch (error) {
    console.error("Error in searchPlayers:", error.message);
    res.status(500).json({ error: error.message });
  }
};

/**
 * Get detailed information about a specific player by ID.
 * Route: GET /players/:id
 */
exports.getPlayerDetails = async (req, res) => {
  const playerId = req.params.id;

  try {
    const result = await playerService.getPlayerDetails(playerId);
    res.json(result);
  } catch (error) {
    console.error("Error in getPlayerDetails:", error.message);
    if (
      error.message.includes("404") ||
      error.message.toLowerCase().includes("not found")
    ) {
      res.status(404).json({ error: "Player not found" });
    } else {
      res.status(500).json({ error: error.message });
    }
  }
};

/**
 * Get recent matches/events for a specific player.
 * Route: GET /players/:id/matches?limit=5
 */
exports.getPlayerMatches = async (req, res) => {
  const playerId = req.params.id;
  const limit = parseInt(req.query.limit) || 5;

  try {
    const result = await playerService.getPlayerMatches(playerId, limit);
    res.json(result);
  } catch (error) {
    console.error("Error in getPlayerMatches:", error.message);
    res.status(500).json({ error: error.message });
  }
};
// controllers/leagueController.js

const axios = require("axios");
const cache = require("../utils/cache"); // Import the cache
const playerService = require("../services/playerService"); // Import the player service

/**
 * Fetches detailed information about a specific league, its teams, standings, top scorers, and live matches.
 * Route: GET /leagues/:code
 * Example: GET /leagues/PL
 */
async function leagueController(req, res) {
  try {
    const leagueCode = req.params.code.toUpperCase(); // Ensure the league code is uppercase (e.g., 'PL')

    // Check if the data for this leagueCode is already in the cache
    const cachedData = cache.get(leagueCode);
    if (cachedData) {
      console.log(`Serving cached data for league: ${leagueCode}`);
      return res.status(200).json(cachedData);
    }

    // Define the API endpoints
    const leagueInfoUrl = `https://api.football-data.org/v4/competitions/${leagueCode}`;
    const leagueTeamsUrl = `https://api.football-data.org/v4/competitions/${leagueCode}/teams`;

    // Make parallel API requests for league info and teams using API_FOOTBALL_KEY1
    const [leagueResponse, teamsResponse] = await Promise.all([
      axios.get(leagueInfoUrl, {
        headers: { "X-Auth-Token": process.env.API_FOOTBALL_KEY1 },
      }),
      axios.get(leagueTeamsUrl, {
        headers: { "X-Auth-Token": process.env.API_FOOTBALL_KEY1 },
      }),
    ]);

    const leagueData = leagueResponse.data;
    const teamsData = teamsResponse.data.teams;
    const competitionId = leagueData.id; // Need competition ID for further API calls

    // Define endpoints for standings and top scorers using API_FOOTBALL_KEY2
    const standingsUrl = `https://api.football-data.org/v4/competitions/${competitionId}/standings`;
    const scorersUrl = `https://api.football-data.org/v4/competitions/${leagueCode}/scorers`;
    const matchesUrl = `https://api.football-data.org/v4/competitions/${competitionId}/matches`;

    // Calculate date range for matches (today to 7 days ahead)
    const today = new Date();
    const dateFrom = today.toISOString().split("T")[0];
    const dateTo = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
      .toISOString()
      .split("T")[0];

    // Make parallel API requests for standings, top scorers, and matches using API_FOOTBALL_KEY2
    const [standingsResponse, scorersResponse, matchesResponse] =
      await Promise.all([
        axios.get(standingsUrl, {
          headers: { "X-Auth-Token": process.env.API_FOOTBALL_KEY1 },
        }),
        axios.get(scorersUrl, {
          headers: { "X-Auth-Token": process.env.API_FOOTBALL_KEY1 },
          params: {
            limit: 10, // Fetch top 10 scorers
          },
        }),
        axios.get(matchesUrl, {
          headers: { "X-Auth-Token": process.env.API_FOOTBALL_KEY1 },
          params: {
            dateFrom,
            dateTo,
          },
        }),
      ]);

    const standingsData = standingsResponse.data;
    const scorersData = scorersResponse.data;
    const matchesData = matchesResponse.data.matches;

    // Process Standings
    let standings = [];
    if (standingsData.standings && standingsData.standings.length > 0) {
      // Assuming the first standing is the overall table
      standings = standingsData.standings[0].table.map((team) => ({
        position: team.position,
        team: {
          id: team.team.id,
          name: team.team.name,
          shortName: team.team.shortName,
          tla: team.team.tla,
          crest: team.team.crest,
        },
        playedGames: team.playedGames,
        won: team.won,
        draw: team.draw,
        lost: team.lost,
        points: team.points,
        goalsFor: team.goalsFor,
        goalsAgainst: team.goalsAgainst,
        goalDifference: team.goalDifference,
      }));
    }

    // Process Top Scorers
    // Fetch detailed player information using playerService
    const topScorersPromises = scorersData.scorers
      .slice(0, 10)
      .map(async (scorer) => {
        try {
          const playerDetails = await playerService.getPlayerDetails(
            scorer.player.id
          );
          return {
            goals: scorer.goals,
            assists: scorer.assists || 0,
            penalties: scorer.penalties || 0,
            player: playerDetails.response, // Detailed player information
            team: scorer.team, // Team information from API_FOOTBALL
          };
        } catch (error) {
          console.error(
            `Error fetching details for player ID "${scorer.player.id}":`,
            error.message
          );
          // Fallback to basic scorer data if detailed info fails
          return {
            goals: scorer.goals,
            assists: scorer.assists || 0,
            penalties: scorer.penalties || 0,
            player: scorer.player, // Basic player info from API_FOOTBALL
            team: scorer.team,
          };
        }
      });

    const topScorers = await Promise.all(topScorersPromises);

    // Process Matches
    const leagueMatchIds = new Set(teamsData.map((team) => team.id));

    const matches = matchesData
      .filter(
        (match) =>
          leagueMatchIds.has(match.homeTeam.id) ||
          leagueMatchIds.has(match.awayTeam.id)
      )
      .map((match) => ({
        id: match.id,
        utcDate: match.utcDate,
        status: match.status,
        matchday: match.matchday,
        stage: match.stage,
        group: match.group,
        lastUpdated: match.lastUpdated,
        homeTeam: {
          id: match.homeTeam.id,
          name: match.homeTeam.name,
          shortName: match.homeTeam.shortName,
          tla: match.homeTeam.tla,
          crest: match.homeTeam.crest,
        },
        awayTeam: {
          id: match.awayTeam.id,
          name: match.awayTeam.name,
          shortName: match.awayTeam.shortName,
          tla: match.awayTeam.tla,
          crest: match.awayTeam.crest,
        },
        score: match.score,
        odds: {
          homeWin: Math.random().toFixed(2), // Mock data
          draw: Math.random().toFixed(2),
          awayWin: Math.random().toFixed(2),
        },
      }));

    // Structure the response
    const responseData = {
      league: {
        id: leagueData.id,
        name: leagueData.name,
        code: leagueData.code,
        type: leagueData.type,
        emblem: leagueData.emblem,
        area: leagueData.area,
        currentSeason: leagueData.currentSeason,
      },
      teams: teamsData.map((team) => ({
        id: team.id,
        name: team.name,
        shortName: team.shortName,
        tla: team.tla,
        crest: team.crest,
        venue: team.venue,
        website: team.website,
        founded: team.founded,
        clubColors: team.clubColors,
      })),
      standings,
      topScorers,
      matches,
    };

    // Store the response data in the cache
    cache.set(leagueCode, responseData);

    console.log(`Cached data for league: ${leagueCode}`);

    // Send the response
    res.status(200).json(responseData);
  } catch (error) {
    console.error("Error fetching league data:", error.message);

    // Handle specific API errors
    if (error.response) {
      const { status, data } = error.response;
      return res.status(status).json({
        message: data.message || "Error fetching league data from API.",
      });
    }

    // Generic server error
    res.status(500).json({
      message: "Unable to retrieve league data. Please try again later.",
    });
  }
}

module.exports = leagueController;

So, my question is that I want top scorers data of each player which will get from my db but problem is there that these api's player id don't meet with each, So how can it provide through it?