const express = require('express');
const path = require('path');
const bodyParser = require('body-parser');
const API = require('./src/js/index.js');
const demoTracker = require('./src/js/demoTracker.js');
const { logger } = require('./src/js/logger.js');
const favicon = require('serve-favicon');
const app = express();
const port = process.env.PORT || 3512;
const {
  processJsonOutput,
  sanitizeHeaders,
  timeoutPromise,
  ensureLogin,
  handleApiError,
  activeSessions,
} = require('./src/js/serverUtils.js');
require('./src/js/utils.js');

app.set('trust proxy', true);

// Middleware
app.use(bodyParser.urlencoded({ extended: true, limit: '10mb' }));
app.use(express.static(__dirname));
app.use(express.static(path.join(__dirname, 'public')));
app.use('/images', express.static(path.join(__dirname, 'src/images')));
app.use(favicon(path.join(__dirname, 'src', 'images', 'favicon.ico')));
app.use(
  bodyParser.json({
    limit: '10mb',
    verify: (req, res, buf) => {
      req.rawBody = buf.toString();
    },
  })
);
app.use(demoTracker.demoModeMiddleware);

// Set up demo mode cleanup interval
setInterval(
  () => demoTracker.demoModeRequestTracker.cleanupExpiredEntries(),
  3600000
); // Clean up every hour

app.get('/health', (req, res) => {
  const clientIP =
    req.headers['cf-connecting-ip'] ||
    req.headers['x-forwarded-for']?.split(',')[0] ||
    req.ip ||
    req.connection.remoteAddress;

  const isDemoMode = demoTracker.isDemoModeActive();
  res.json({
    status: 'ok',
    timestamp: global.Utils.toIsoString(new Date()),
    demoMode: isDemoMode,
    requestsRemaining:
      isDemoMode ?
        demoTracker.demoModeRequestTracker.maxRequestsPerHour -
        (demoTracker.demoModeRequestTracker.requests.get(clientIP)?.count || 0)
      : null,
  });
});

// Serve the main HTML file
app.get('/', async (req, res) => {
  // Check demo mode query parameter
  if (req.query.demo === 'true') {
    await demoTracker.initializeDemoMode(true);
  } else if (req.query.demo === 'false') {
    await demoTracker.initializeDemoMode(false);
  }

  res.sendFile(path.join(__dirname, 'src', 'index.html'));
});

// Demo mode route - enables demo mode when visited
app.get('/demo', async (req, res) => {
  const success = await demoTracker.initializeDemoMode(true);

  if (success) {
    logger.info('Demo mode activated via /demo route');
    res.redirect('/?demo=true');
  } else {
    res
      .status(500)
      .send('Failed to enable demo mode. Check server logs for details.');
  }
});

// Demo mode deactivation route
app.get('/demo/disable', async (req, res) => {
  await demoTracker.initializeDemoMode(false);
  logger.info('Demo mode deactivated via /demo/disable route');
  res.redirect('/?demo=false');
});

// API endpoint to fetch stats
app.post('/api/stats', async (req, res) => {
  logger.debug('Received request for /api/stats');
  logger.debug(
    `Request IP: ${
      req.headers['x-forwarded-for'] || req.ip || req.connection.remoteAddress
    }`
  );
  logger.debug(
    `Request JSON: ${JSON.stringify({
      username: req.body.username,
      platform: req.body.platform,
      game: req.body.game,
      apiCall: req.body.apiCall,
      sanitize: req.body.sanitize,
      replaceKeys: req.body.replaceKeys,
    })}`
  );

  try {
    let { username, ssoToken, platform, game, apiCall } = req.body;

    const defaultToken = demoTracker.getDefaultSsoToken();
    if (!ssoToken && defaultToken) {
      ssoToken = defaultToken;
      logger.info('Using default SSO token for demo mode');
    } else if (!ssoToken) {
      return res.status(200).json({
        status: 'error',
        message: 'SSO Token is required',
        timestamp: global.Utils.toIsoString(new Date()),
      });
    }

    /*
    logger.debug(
      `Request details - Username: ${username}, Platform: ${platform}, Game: ${game}, API Call: ${apiCall}`
    );

    logger.debug("=== STATS REQUEST ===");
    logger.debug(`Username: ${username || 'Not provided'}`);
    logger.debug(`Platform: ${platform}`);
    logger.debug(`Game: ${game}`);
    logger.debug(`API Call: ${apiCall}`);
    logger.debug(`Processing Options - Sanitize: ${sanitize}, Replace Keys: ${replaceKeys}`);
    logger.debug("====================="); */

    /*
    if (!ssoToken) {
      return res.status(400).json({ error: 'SSO Token is required' });
    } */

    // Clear previous session if it exists
    if (activeSessions.has(ssoToken)) {
      logger.debug('Clearing previous session');
      activeSessions.delete(ssoToken);
    }

    // Login with the provided SSO token
    try {
      await ensureLogin(ssoToken);
    } catch (loginError) {
      logger.error('Login error:', loginError);
      return res.status(200).json({
        status: 'error',
        error_type: 'LoginError',
        message: 'SSO token login failed',
        details: loginError.message || 'Unknown login error',
        timestamp: global.Utils.toIsoString(new Date()),
      });
    }

    // Create a wrapped function for each API call to handle timeout
    const fetchWithTimeout = async (apiFn) => {
      return Promise.race([
        apiFn(),
        timeoutPromise(30000), // 30 second timeout
      ]);
    };

    // Check if the platform is valid for the game
    const requiresUno = ['mw2', 'wz2', 'mw3', 'wzm'].includes(game);
    if (requiresUno && platform !== 'uno') {
      logger.warn(`${game} requires Uno ID`);
      return res.status(200).json({
        status: 'error',
        message: `${game} requires Uno ID (numerical ID)`,
        timestamp: global.Utils.toIsoString(new Date()),
      });
    }

    try {
      logger.debug(
        `Attempting to fetch ${game} data for ${username} on ${platform}`
      );
      let data;

      if (apiCall === 'fullData') {
        // Fetch lifetime stats based on game
        switch (game) {
          case 'mw':
            data = await fetchWithTimeout(() =>
              API.ModernWarfare.fullData(username, platform)
            );
            break;
          case 'wz':
            data = await fetchWithTimeout(() =>
              API.Warzone.fullData(username, platform)
            );
            break;
          case 'mw2':
            data = await fetchWithTimeout(() =>
              API.ModernWarfare2.fullData(username)
            );
            break;
          case 'wz2':
            data = await fetchWithTimeout(() =>
              API.Warzone2.fullData(username)
            );
            break;
          case 'mw3':
            data = await fetchWithTimeout(() =>
              API.ModernWarfare3.fullData(username)
            );
            break;
          case 'cw':
            data = await fetchWithTimeout(() =>
              API.ColdWar.fullData(username, platform)
            );
            break;
          case 'vg':
            data = await fetchWithTimeout(() =>
              API.Vanguard.fullData(username, platform)
            );
            break;
          case 'wzm':
            data = await fetchWithTimeout(() =>
              API.WarzoneMobile.fullData(username)
            );
            break;
          default:
            return res.status(200).json({
              status: 'error',
              message: 'Invalid game selected',
              timestamp: global.Utils.toIsoString(new Date()),
            });
        }
      } else if (apiCall === 'combatHistory') {
        // Fetch recent match history based on game
        switch (game) {
          case 'mw':
            data = await fetchWithTimeout(() =>
              API.ModernWarfare.combatHistory(username, platform)
            );
            break;
          case 'wz':
            data = await fetchWithTimeout(() =>
              API.Warzone.combatHistory(username, platform)
            );
            break;
          case 'mw2':
            data = await fetchWithTimeout(() =>
              API.ModernWarfare2.combatHistory(username)
            );
            break;
          case 'wz2':
            data = await fetchWithTimeout(() =>
              API.Warzone2.combatHistory(username)
            );
            break;
          case 'mw3':
            data = await fetchWithTimeout(() =>
              API.ModernWarfare3.combatHistory(username)
            );
            break;
          case 'cw':
            data = await fetchWithTimeout(() =>
              API.ColdWar.combatHistory(username, platform)
            );
            break;
          case 'vg':
            data = await fetchWithTimeout(() =>
              API.Vanguard.combatHistory(username, platform)
            );
            break;
          case 'wzm':
            data = await fetchWithTimeout(() =>
              API.WarzoneMobile.combatHistory(username)
            );
            break;
          default:
            return res.status(200).json({
              status: 'error',
              message: 'Invalid game selected',
              timestamp: global.Utils.toIsoString(new Date()),
            });
        }
      }

      logger.debug('Data fetched successfully');
      logger.debug(`Response Size: ~${JSON.stringify(data).length / 1024} KB`);
      logger.debug(`Response Time: ${global.Utils.toIsoString(new Date())}`);

      // Safely handle the response data
      if (!data) {
        logger.warn('No data returned from API');
        return res.json({
          status: 'partial_success',
          message: 'No data returned from API, but no error thrown',
          data: null,
          timestamp: global.Utils.toIsoString(new Date()),
        });
      }

      // logger.debug("Returning data to client");

      const { sanitize, replaceKeys } = req.body;

      demoTracker.incrementDemoCounter(req, ssoToken);

      return res.json({
        // status: "success",
        data: processJsonOutput(data, { sanitize, replaceKeys }),
        timestamp: global.Utils.toIsoString(new Date()),
      });
    } catch (apiError) {
      return handleApiError(apiError, res);
    }
  } catch (serverError) {
    logger.error('Server Error:', serverError);

    // Return a structured error response even for unexpected errors
    return res.status(200).json({
      status: 'server_error',
      message: 'The server encountered an unexpected error',
      error_details: serverError.message || 'Unknown server error',
      timestamp: global.Utils.toIsoString(new Date()),
    });
  }
});

// API endpoint to fetch recent matches
app.post('/api/matches', async (req, res) => {
  logger.debug('Received request for /api/matches');
  logger.debug(
    `Request IP: ${
      req.headers['x-forwarded-for'] || req.ip || req.connection.remoteAddress
    }`
  );
  logger.debug(
    `Request JSON: ${JSON.stringify({
      username: req.body.username,
      platform: req.body.platform,
      game: req.body.game,
      sanitize: req.body.sanitize,
      replaceKeys: req.body.replaceKeys,
    })}`
  );

  try {
    let { username, ssoToken, platform, game } = req.body;

    /*
    logger.debug(
       `Request details - Username: ${username}, Platform: ${platform}, Game: ${game}`
    );

    logger.debug("=== MATCHES REQUEST ===");
    logger.debug(`Username: ${username}`);
    logger.debug(`Platform: ${platform}`);
    logger.debug(`Game: ${game}`);
    logger.debug(`Processing Options - Sanitize: ${sanitize}, Replace Keys: ${replaceKeys}`);
    logger.debug("========================"); */

    const defaultToken = demoTracker.getDefaultSsoToken();
    if (!ssoToken && defaultToken) {
      ssoToken = defaultToken;
      logger.info('Using default SSO token for demo mode');
    } else if (!ssoToken) {
      return res.status(200).json({
        status: 'error',
        message: 'SSO Token is required',
        timestamp: global.Utils.toIsoString(new Date()),
      });
    }

    if (!username) {
      return res.status(400).json({ error: 'Username is required' });
    }

    if (!username || !ssoToken) {
      return res
        .status(400)
        .json({ error: 'Username and SSO Token are required' });
    }

    try {
      await ensureLogin(ssoToken);
    } catch (loginError) {
      return res.status(200).json({
        status: 'error',
        error_type: 'LoginError',
        message: 'SSO token login failed',
        details: loginError.message || 'Unknown login error',
        timestamp: global.Utils.toIsoString(new Date()),
      });
    }

    // Create a wrapped function for each API call to handle timeout
    const fetchWithTimeout = async (apiFn) => {
      return Promise.race([
        apiFn(),
        timeoutPromise(30000), // 30 second timeout
      ]);
    };

    try {
      logger.debug(
        `Attempting to fetch combat history for ${username} on ${platform}`
      );
      let data;

      // Check if the platform is valid for the game
      const requiresUno = ['mw2', 'wz2', 'mw3', 'wzm'].includes(game);
      if (requiresUno && platform !== 'uno') {
        return res.status(200).json({
          status: 'error',
          message: `${game} requires Uno ID (numerical ID)`,
          timestamp: global.Utils.toIsoString(new Date()),
        });
      }

      // Fetch combat history based on game
      switch (game) {
        case 'mw':
          data = await fetchWithTimeout(() =>
            API.ModernWarfare.combatHistory(username, platform)
          );
          break;
        case 'wz':
          data = await fetchWithTimeout(() =>
            API.Warzone.combatHistory(username, platform)
          );
          break;
        case 'mw2':
          data = await fetchWithTimeout(() =>
            API.ModernWarfare2.combatHistory(username)
          );
          break;
        case 'wz2':
          data = await fetchWithTimeout(() =>
            API.Warzone2.combatHistory(username)
          );
          break;
        case 'mw3':
          data = await fetchWithTimeout(() =>
            API.ModernWarfare3.combatHistory(username)
          );
          break;
        case 'cw':
          data = await fetchWithTimeout(() =>
            API.ColdWar.combatHistory(username, platform)
          );
          break;
        case 'vg':
          data = await fetchWithTimeout(() =>
            API.Vanguard.combatHistory(username, platform)
          );
          break;
        case 'wzm':
          data = await fetchWithTimeout(() =>
            API.WarzoneMobile.combatHistory(username)
          );
          break;
        default:
          return res.status(200).json({
            status: 'error',
            message: 'Invalid game selected',
            timestamp: global.Utils.toIsoString(new Date()),
          });
      }

      const { sanitize, replaceKeys } = req.body;

      demoTracker.incrementDemoCounter(req, ssoToken);

      return res.json({
        // status: "success",
        data: processJsonOutput(data, { sanitize, replaceKeys }),
        timestamp: global.Utils.toIsoString(new Date()),
      });
    } catch (apiError) {
      return handleApiError(apiError, res);
    }
  } catch (serverError) {
    return res.status(200).json({
      status: 'server_error',
      message: 'The server encountered an unexpected error',
      error_details: serverError.message || 'Unknown server error',
      timestamp: global.Utils.toIsoString(new Date()),
    });
  }
});

// API endpoint to fetch match info
app.post('/api/matchInfo', async (req, res) => {
  logger.debug('Received request for /api/matchInfo');
  logger.debug(
    `Request IP: ${
      req.headers['x-forwarded-for'] || req.ip || req.connection.remoteAddress
    }`
  );
  logger.debug(
    `Request JSON: ${JSON.stringify({
      matchId: req.body.matchId,
      platform: req.body.platform,
      game: req.body.game,
      sanitize: req.body.sanitize,
      replaceKeys: req.body.replaceKeys,
    })}`
  );

  try {
    let { matchId, ssoToken, platform, game } = req.body;

    /*
    logger.debug(
      `Request details - Match ID: ${matchId}, Platform: ${platform}, Game: ${game}`
    );

    logger.debug("=== MATCH INFO REQUEST ===");
    logger.debug(`Match ID: ${matchId}`);
    logger.debug(`Platform: ${platform}`);
    logger.debug(`Game: ${game}`);
    logger.debug(`Processing Options - Sanitize: ${sanitize}, Replace Keys: ${replaceKeys}`);
    logger.debug("=========================="); */

    const defaultToken = demoTracker.getDefaultSsoToken();
    if (!ssoToken && defaultToken) {
      ssoToken = defaultToken;
      logger.info('Using default SSO token for demo mode');
    } else if (!ssoToken) {
      return res.status(200).json({
        status: 'error',
        message: 'SSO Token is required',
        timestamp: global.Utils.toIsoString(new Date()),
      });
    }

    if (!matchId) {
      return res.status(400).json({ error: 'Match ID is required' });
    }

    if (!matchId || !ssoToken) {
      return res
        .status(400)
        .json({ error: 'Match ID and SSO Token are required' });
    }

    try {
      await ensureLogin(ssoToken);
    } catch (loginError) {
      return res.status(200).json({
        status: 'error',
        error_type: 'LoginError',
        message: 'SSO token login failed',
        details: loginError.message || 'Unknown login error',
        timestamp: global.Utils.toIsoString(new Date()),
      });
    }

    // Create a wrapped function for each API call to handle timeout
    const fetchWithTimeout = async (apiFn) => {
      return Promise.race([
        apiFn(),
        timeoutPromise(30000), // 30 second timeout
      ]);
    };

    try {
      logger.debug(`Attempting to fetch match info for match ID: ${matchId}`);
      let data;

      // Fetch match info based on game
      switch (game) {
        case 'mw':
          data = await fetchWithTimeout(() =>
            API.ModernWarfare.matchInfo(matchId, platform)
          );
          break;
        case 'wz':
          data = await fetchWithTimeout(() =>
            API.Warzone.matchInfo(matchId, platform)
          );
          break;
        case 'mw2':
          data = await fetchWithTimeout(() =>
            API.ModernWarfare2.matchInfo(matchId)
          );
          break;
        case 'wz2':
          data = await fetchWithTimeout(() => API.Warzone2.matchInfo(matchId));
          break;
        case 'mw3':
          data = await fetchWithTimeout(() =>
            API.ModernWarfare3.matchInfo(matchId)
          );
          break;
        case 'cw':
          data = await fetchWithTimeout(() =>
            API.ColdWar.matchInfo(matchId, platform)
          );
          break;
        case 'vg':
          data = await fetchWithTimeout(() =>
            API.Vanguard.matchInfo(matchId, platform)
          );
          break;
        case 'wzm':
          data = await fetchWithTimeout(() =>
            API.WarzoneMobile.matchInfo(matchId)
          );
          break;
        default:
          return res.status(200).json({
            status: 'error',
            message: 'Invalid game selected',
            timestamp: global.Utils.toIsoString(new Date()),
          });
      }

      const { sanitize, replaceKeys } = req.body;

      demoTracker.incrementDemoCounter(req, ssoToken);

      return res.json({
        // status: "success",
        data: processJsonOutput(data, { sanitize, replaceKeys }),
        timestamp: global.Utils.toIsoString(new Date()),
      });
    } catch (apiError) {
      return handleApiError(apiError, res);
    }
  } catch (serverError) {
    return res.status(200).json({
      status: 'server_error',
      message: 'The server encountered an unexpected error',
      error_details: serverError.message || 'Unknown server error',
      timestamp: global.Utils.toIsoString(new Date()),
    });
  }
});

// API endpoint for user-related API calls
app.post('/api/user', async (req, res) => {
  logger.debug('Received request for /api/user');
  logger.debug(
    `Request IP: ${
      req.headers['x-forwarded-for'] || req.ip || req.connection.remoteAddress
    }`
  );
  logger.debug(
    `Request JSON: ${JSON.stringify({
      username: req.body.username,
      platform: req.body.platform,
      userCall: req.body.userCall,
      sanitize: req.body.sanitize,
      replaceKeys: req.body.replaceKeys,
    })}`
  );

  try {
    let { username, ssoToken, platform, userCall } = req.body;

    /*
    logger.debug(
      `Request details - Username: ${username}, Platform: ${platform}, User Call: ${userCall}`
    );

    logger.debug("=== USER DATA REQUEST ===");
    logger.debug(`Username: ${username || 'Not provided'}`);
    logger.debug(`Platform: ${platform}`);
    logger.debug(`User Call: ${userCall}`);
    logger.debug(`Processing Options - Sanitize: ${sanitize}, Replace Keys: ${replaceKeys}`);
    logger.debug("========================="); */

    const defaultToken = demoTracker.getDefaultSsoToken();
    if (!ssoToken && defaultToken) {
      ssoToken = defaultToken;
      logger.info('Using default SSO token for demo mode');
    } else if (!ssoToken) {
      return res.status(200).json({
        status: 'error',
        message: 'SSO Token is required',
        timestamp: global.Utils.toIsoString(new Date()),
      });
    }

    // For eventFeed and identities, username is not required
    if (
      !username &&
      userCall !== 'eventFeed' &&
      userCall !== 'friendFeed' &&
      userCall !== 'identities'
    ) {
      return res
        .status(400)
        .json({ error: 'Username is required for this API call' });
    }

    try {
      await ensureLogin(ssoToken);
    } catch (loginError) {
      return res.status(200).json({
        status: 'error',
        error_type: 'LoginError',
        message: 'SSO token login failed',
        details: loginError.message || 'Unknown login error',
        timestamp: global.Utils.toIsoString(new Date()),
      });
    }

    // Create a wrapped function for each API call to handle timeout
    const fetchWithTimeout = async (apiFn) => {
      return Promise.race([
        apiFn(),
        timeoutPromise(30000), // 30 second timeout
      ]);
    };

    try {
      logger.debug(`Attempting to fetch user data for ${userCall}`);
      let data;

      // Fetch user data based on userCall
      switch (userCall) {
        case 'codPoints':
          data = await fetchWithTimeout(() =>
            API.Me.codPoints(username, platform)
          );
          break;
        case 'connectedAccounts':
          data = await fetchWithTimeout(() =>
            API.Me.connectedAccounts(username, platform)
          );
          break;
        case 'eventFeed':
          data = await fetchWithTimeout(() => API.Me.eventFeed());
          break;
        case 'friendFeed':
          data = await fetchWithTimeout(() =>
            API.Me.friendFeed(username, platform)
          );
          break;
        case 'identities':
          data = await fetchWithTimeout(() => API.Me.loggedInIdentities());
          break;
        case 'friendsList':
          data = await fetchWithTimeout(() => API.Me.friendsList());
          break;
        case 'userInfo':
          data = await fetchWithTimeout(() => API.Me.userInfo());
          break;
        // case "settings":
        //   data = await fetchWithTimeout(() =>
        //     API.Me.settings(username, platform)
        //   );
        //   break;
        default:
          return res.status(200).json({
            status: 'error',
            message: 'Invalid user API call selected',
            timestamp: global.Utils.toIsoString(new Date()),
          });
      }

      const { sanitize, replaceKeys } = req.body;

      demoTracker.incrementDemoCounter(req, ssoToken);

      return res.json({
        // status: "success",
        data: processJsonOutput(data, { sanitize, replaceKeys }),
        timestamp: global.Utils.toIsoString(new Date()),
      });
    } catch (apiError) {
      return handleApiError(apiError, res);
    }
  } catch (serverError) {
    return res.status(200).json({
      status: 'server_error',
      message: 'The server encountered an unexpected error',
      error_details: serverError.message || 'Unknown server error',
      timestamp: global.Utils.toIsoString(new Date()),
    });
  }
});

// API endpoint for CDN-related API calls
app.post('/api/cdn', async (req, res) => {
  logger.debug('Received request for /api/cdn');
  logger.debug(
    `Request IP: ${
      req.headers['x-forwarded-for'] || req.ip || req.connection.remoteAddress
    }`
  );
  logger.debug(
    `Request JSON: ${JSON.stringify({
      cdnCall: req.body.cdnCall,
    })}`
  );

  try {
    let { ssoToken, cdnCall } = req.body;

    /*
    logger.debug(
      `Request details - User Call: ${cdnCall}`
    );

    logger.debug("=== USER DATA REQUEST ===");
    logger.debug(`CDN Call: ${cdnCall}`);
    logger.debug("========================="); */

    const defaultToken = demoTracker.getDefaultSsoToken();
    if (!ssoToken && defaultToken) {
      ssoToken = defaultToken;
      logger.info('Using default SSO token for demo mode');
    } else if (!ssoToken) {
      return res.status(200).json({
        status: 'error',
        message: 'SSO Token is required',
        timestamp: global.Utils.toIsoString(new Date()),
      });
    }

    try {
      await ensureLogin(ssoToken);
    } catch (loginError) {
      return res.status(200).json({
        status: 'error',
        error_type: 'LoginError',
        message: 'SSO token login failed',
        details: loginError.message || 'Unknown login error',
        timestamp: global.Utils.toIsoString(new Date()),
      });
    }

    // Create a wrapped function for each API call to handle timeout
    const fetchWithTimeout = async (apiFn) => {
      return Promise.race([
        apiFn(),
        timeoutPromise(30000), // 30 second timeout
      ]);
    };

    try {
      logger.debug(`Attempting to fetch CDN data for ${cdnCall}`);
      let data;
      let platform = API.platforms.Activision;

      // Fetch user data based on cdnCall
      switch (cdnCall) {
        case 'mapList':
          data = await fetchWithTimeout(() =>
            API.ModernWarfare.mapList(platform)
          );
          break;
        case 'accolades':
          data = await fetchWithTimeout(() => API.CDN.accolades());
          break;
        case 'allCDNData':
          data = await fetchWithTimeout(() => API.CDN.allCDNData());
          break;

        default:
          return res.status(200).json({
            status: 'error',
            message: 'Invalid user API call selected',
            timestamp: global.Utils.toIsoString(new Date()),
          });
      }

      demoTracker.incrementDemoCounter(req, ssoToken);

      return res.json({
        // status: "success",
        data: data,
        timestamp: global.Utils.toIsoString(new Date()),
      });
    } catch (apiError) {
      return handleApiError(apiError, res);
    }
  } catch (serverError) {
    return res.status(200).json({
      status: 'server_error',
      message: 'The server encountered an unexpected error',
      error_details: serverError.message || 'Unknown server error',
      timestamp: global.Utils.toIsoString(new Date()),
    });
  }
});

// API endpoint for fuzzy search
app.post('/api/search', async (req, res) => {
  logger.debug('Received request for /api/search');
  logger.debug(
    `Request IP: ${
      req.headers['x-forwarded-for'] || req.ip || req.connection.remoteAddress
    }`
  );
  logger.debug(
    `Request JSON: ${JSON.stringify({
      username: req.body.username,
      platform: req.body.platform,
      sanitize: req.body.sanitize,
      replaceKeys: req.body.replaceKeys,
    })}`
  );

  try {
    let { username, ssoToken, platform } = req.body;

    /*
    logger.debug(
      `Request details - Match ID: ${matchId}, Platform: ${platform}, Game: ${game}`
    );

    logger.debug("=== MATCH INFO REQUEST ===");
    logger.debug(`Match ID: ${matchId}`);
    logger.debug(`Platform: ${platform}`);
    logger.debug(`Game: ${game}`);
    logger.debug(`Processing Options - Sanitize: ${sanitize}, Replace Keys: ${replaceKeys}`);
    logger.debug("=========================="); */

    const defaultToken = demoTracker.getDefaultSsoToken();
    if (!ssoToken && defaultToken) {
      ssoToken = defaultToken;
      logger.info('Using default SSO token for demo mode');
    } else if (!ssoToken) {
      return res.status(200).json({
        status: 'error',
        message: 'SSO Token is required',
        timestamp: global.Utils.toIsoString(new Date()),
      });
    }

    if (!username) {
      return res.status(400).json({ error: 'Username is required' });
    }

    if (!username || !ssoToken) {
      return res
        .status(400)
        .json({ error: 'Username and SSO Token are required' });
    }

    try {
      await ensureLogin(ssoToken);
    } catch (loginError) {
      return res.status(200).json({
        status: 'error',
        error_type: 'LoginError',
        message: 'SSO token login failed',
        details: loginError.message || 'Unknown login error',
        timestamp: global.Utils.toIsoString(new Date()),
      });
    }

    // Create a wrapped function for each API call to handle timeout
    const fetchWithTimeout = async (apiFn) => {
      return Promise.race([
        apiFn(),
        timeoutPromise(30000), // 30 second timeout
      ]);
    };

    try {
      logger.debug(
        `Attempting fuzzy search for ${username} on platform ${platform}`
      );
      const data = await fetchWithTimeout(() =>
        API.Misc.search(username, platform)
      );

      const { sanitize, replaceKeys } = req.body;

      demoTracker.incrementDemoCounter(req, ssoToken);

      return res.json({
        // status: "success",
        data: processJsonOutput(data, { sanitize, replaceKeys }),
        timestamp: global.Utils.toIsoString(new Date()),
        // link: "Stats pulled using codtracker.rimmyscorner.com",
      });
    } catch (apiError) {
      return handleApiError(apiError, res);
    }
  } catch (serverError) {
    return res.status(200).json({
      status: 'server_error',
      message: 'The server encountered an unexpected error',
      error_details: serverError.message || 'Unknown server error',
      timestamp: global.Utils.toIsoString(new Date()),
    });
  }
});

// Improved logging endpoint
app.post('/api/log', (req, res) => {
  const clientIP =
    req.headers['cf-connecting-ip'] ||
    req.headers['x-forwarded-for']?.split(',')[0] ||
    req.ip ||
    req.connection.remoteAddress;
  const userAgent = req.headers['user-agent'];
  const referer = req.headers['referer'];
  const origin = req.headers['origin'];
  let logData;

  try {
    // Handle data whether it comes as already parsed JSON or as a string
    if (typeof req.body === 'string') {
      logData = JSON.parse(req.body);
    } else if (req.body && typeof req.body === 'object') {
      logData = req.body;
    } else {
      // If no parsable data found, create a basic log entry
      logData = {
        eventType: 'unknown',
        timestamp: global.Utils.toIsoString(new Date()),
      };
    }

    // Enrich log with server-side data
    const enrichedLog = {
      ...logData,
      meta: {
        clientIP,
        userAgent,
        referer,
        origin,
        requestHeaders: sanitizeHeaders(req.headers),
        serverTimestamp: global.Utils.toIsoString(new Date()),
        requestId: req.id || Math.random().toString(36).substring(2, 15),
      },
    };

    // Use the dedicated user activity logger
    logger.userActivity(enrichedLog.eventType || 'unknown', enrichedLog);
  } catch (error) {
    logger.error('Error processing log data', {
      error: error.message,
      rawBody: typeof req.body === 'object' ? '[Object]' : req.body,
    });
  }

  // Always return 200 to avoid client-side errors
  res.status(200).send();
});

// Database storage function
/*
function storeLogInDatabase(logData) {
  // Example with MongoDB
  db.collection('user_logs').insertOne(logData)
    .catch(err => logger.error('Failed to store log in database:', err));
}
*/

// Start the server
app.listen(port, () => {
  logger.info(`Server running on http://localhost:${port}`);
});
