import { nanoid } from 'nanoid';
import { readFileSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
import Game from '../models/Game.js';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

// Функция для выбора случайных 5 категорий (из всех доступных)
const selectRandomCategories = (allCategories, usedCategoryIds) => {
  const availableCategories = allCategories.filter(
    cat => !usedCategoryIds.includes(cat.id)
  );

  // Перемешиваем и берем первые 5
  const shuffled = availableCategories.sort(() => Math.random() - 0.5);
  return shuffled.slice(0, 5);
};

// Функция проверки, закончилась ли игра
const checkGameFinished = (game, allCategories) => {
  // Проверяем, остались ли доступные категории
  const availableCategories = allCategories.filter(
    cat => !game.usedCategoryIds.includes(cat.id)
  );

  // Проверяем, все ли вопросы текущего раунда использованы
  const totalQuestionsInRound = game.selectedCategories.reduce(
    (sum, cat) => sum + cat.questions.length,
    0
  );
  const allQuestionsUsed = game.usedQuestions.length >= totalQuestionsInRound;

  // Игра заканчивается если:
  // 1. Все вопросы текущего раунда использованы
  // 2. И нет больше доступных категорий для следующего раунда
  return allQuestionsUsed && availableCategories.length === 0;
};

// Функция для отправки обновления игры (без timerInterval)
const sendGameUpdate = (io, gameCode, game) => {
  const { timerInterval, ...gameToSend } = game;
  io.to(gameCode).emit('game-updated', gameToSend);
};

export const setupSocketHandlers = (io, activeGames) => {
  io.on('connection', (socket) => {
    console.log('👤 Client connected:', socket.id);

    // Создание игры
    socket.on('create-game', async ({ hostName }) => {
      const gameCode = nanoid(6).toUpperCase();

      // Загружаем все категории
      const questionsPath = join(__dirname, '../data/questions.json');
      const questionsData = JSON.parse(readFileSync(questionsPath, 'utf-8'));

      console.log(`📚 Total categories available: ${questionsData.categories.length}`);

      // Выбираем 5 случайных категорий
      const selectedCategories = selectRandomCategories(questionsData.categories, []);

      console.log(`🎲 Selected ${selectedCategories.length} categories for game ${gameCode}`);

      const gameData = {
        gameCode,
        hostId: socket.id,
        hostName: hostName,
        teams: [],
        currentRound: 1,
        selectedCategories: selectedCategories,
        usedCategoryIds: selectedCategories.map(cat => cat.id),
        usedQuestions: [],
        status: 'waiting',
        currentQuestion: null,
        answeringTeam: null,
        timer: null
      };

      // Сохраняем в MongoDB
      try {
        await Game.create(gameData);
        console.log(`💾 Game ${gameCode} saved to DB`);
      } catch (error) {
        console.log('⚠️  Error saving to DB:', error.message);
      }

      activeGames.set(gameCode, gameData);
      socket.join(gameCode);

      socket.emit('game-created', { gameCode, game: gameData });
      console.log(`🎮 Game created: ${gameCode} by host ${hostName}`);
    });

    // Ведущий присоединяется к игре
    socket.on('host-join-game', async ({ gameCode }) => {
      let game = activeGames.get(gameCode);

      // Если игры нет в памяти, пытаемся загрузить из БД
      if (!game) {
        try {
          const dbGame = await Game.findOne({ gameCode }).lean();
          if (dbGame) {
            game = { ...dbGame, timer: null, timerInterval: null };
            activeGames.set(gameCode, game);
            console.log(`📥 Game ${gameCode} loaded from DB`);
          }
        } catch (error) {
          console.log('⚠️  Error loading from DB:', error.message);
        }
      }

      if (!game) {
        socket.emit('error', { message: 'Игра не найдена' });
        return;
      }

      // Обновляем hostId на текущий socket
      game.hostId = socket.id;

      // Обновляем в БД
      try {
        await Game.findOneAndUpdate({ gameCode }, { hostId: socket.id });
      } catch (error) {
        console.log('⚠️  Error updating DB:', error.message);
      }

      socket.join(gameCode);
      socket.emit('host-game-loaded', game);

      console.log(`🎤 Host joined game ${gameCode}`);
    });

    // Присоединение к игре
    socket.on('join-game', async ({ gameCode, teamName }) => {
      let game = activeGames.get(gameCode);

      // Если игры нет в памяти, пытаемся загрузить из БД
      if (!game) {
        try {
          const dbGame = await Game.findOne({ gameCode }).lean();
          if (dbGame) {
            game = { ...dbGame, timer: null, timerInterval: null };
            activeGames.set(gameCode, game);
            console.log(`📥 Game ${gameCode} loaded from DB for join`);
          }
        } catch (error) {
          console.log('⚠️  Error loading from DB:', error.message);
        }
      }

      if (!game) {
        socket.emit('error', { message: 'Игра не найдена' });
        return;
      }

      let team = game.teams.find(t => t.name === teamName);

      if (!team) {
        // Создаем новую команду
        team = {
          id: nanoid(),
          name: teamName,
          score: 0,
          players: []
        };
        game.teams.push(team);
      }

      // Добавляем socket id в команду (без имени игрока)
      team.players.push({ id: socket.id });

      // Обновляем в БД
      try {
        await Game.findOneAndUpdate({ gameCode }, { teams: game.teams });
      } catch (error) {
        console.log('⚠️  Error updating teams in DB:', error.message);
      }

      socket.join(gameCode);
      sendGameUpdate(io, gameCode, game);

      console.log(`👥 Team ${teamName} joined game ${gameCode}`);
    });

    // Начать игру
    socket.on('start-game', async ({ gameCode }) => {
      const game = activeGames.get(gameCode);
      if (game) {
        game.status = 'playing';

        // Обновляем в БД
        try {
          await Game.findOneAndUpdate({ gameCode }, { status: 'playing' });
        } catch (error) {
          console.log('⚠️  Error updating status in DB:', error.message);
        }

        io.to(gameCode).emit('game-started', game);
      }
    });

    // Выбор вопроса
    socket.on('select-question', ({ gameCode, category, points }) => {
      const game = activeGames.get(gameCode);

      if (!game) return;

      // Находим категорию
      const categoryData = game.selectedCategories.find(cat => cat.name === category);

      // Подсчитываем, сколько вопросов с такими баллами уже использовано в этой категории
      const usedCount = game.usedQuestions.filter(
        q => q.category === category && q.points === points
      ).length;

      // Находим следующий неиспользованный вопрос с такими баллами (по порядку)
      const questionsWithPoints = categoryData?.questions
        .map((q, idx) => ({ ...q, originalIndex: idx }))
        .filter(q => q.points === points);

      const questionIndex = questionsWithPoints[usedCount]?.originalIndex;

      console.log(`📊 Select question: category="${category}", points=${points}, usedCount=${usedCount}, questionIndex=${questionIndex}`);
      console.log(`📊 Questions with these points:`, questionsWithPoints.map(q => q.originalIndex));

      if (questionIndex === undefined) {
        console.log('❌ No available question found');
        socket.emit('error', { message: 'Этот вопрос уже был использован' });
        return;
      }

      game.currentQuestion = { category, points, questionIndex };
      console.log(`✅ Selected question:`, game.currentQuestion);
      game.answeringTeam = null;
      game.answeredTeams = []; // Список команд, которые уже отвечали на этот вопрос

      // Получаем тип вопроса
      const questionData = categoryData?.questions[questionIndex];
      const questionType = questionData?.type || 'text';

      game.timer = 60; // 60 секунд на ответ
      game.timerStarted = false; // Флаг, запущен ли таймер

      io.to(gameCode).emit('question-selected', {
        category,
        points,
        questionIndex,
        questionType,
        timer: game.timer
      });

      // Для текстовых и картинок запускаем таймер сразу
      // Для аудио и видео - ждем события 'media-ended' от клиента
      if (questionType === 'text' || questionType === 'image') {
        console.log(`⏱️ Starting timer immediately for ${questionType} question`);
        game.timerStarted = true;

        const timerInterval = setInterval(() => {
          game.timer--;
          io.to(gameCode).emit('timer-update', { timer: game.timer });

          if (game.timer <= 0) {
            clearInterval(timerInterval);
            io.to(gameCode).emit('time-up');
            game.currentQuestion = null;
            game.answeringTeam = null;
            game.answeredTeams = [];
          }
        }, 1000);

        game.timerInterval = timerInterval;
      } else {
        console.log(`⏸️ Waiting for media to end before starting timer (${questionType} question)`);
      }
    });

    // Событие окончания проигрывания медиа (аудио/видео)
    socket.on('media-ended', ({ gameCode }) => {
      const game = activeGames.get(gameCode);

      if (!game || !game.currentQuestion || game.timerStarted) return;

      console.log(`🎬 Media ended, starting timer now`);
      game.timerStarted = true;

      // Запускаем таймер
      const timerInterval = setInterval(() => {
        game.timer--;
        io.to(gameCode).emit('timer-update', { timer: game.timer });

        if (game.timer <= 0) {
          clearInterval(timerInterval);
          io.to(gameCode).emit('time-up');
          game.currentQuestion = null;
          game.answeringTeam = null;
          game.answeredTeams = [];
        }
      }, 1000);

      game.timerInterval = timerInterval;
    });

    // Нажатие кнопки "Ответить"
    socket.on('buzz-in', ({ gameCode, teamId }) => {
      const game = activeGames.get(gameCode);

      if (!game || !game.currentQuestion || game.answeringTeam) return;

      // Проверяем, не отвечала ли уже эта команда на этот вопрос
      if (game.answeredTeams && game.answeredTeams.includes(teamId)) {
        return; // Команда уже отвечала, игнорируем
      }

      game.answeringTeam = teamId;

      // Останавливаем таймер
      if (game.timerInterval) {
        clearInterval(game.timerInterval);
      }

      const team = game.teams.find(t => t.id === teamId);
      io.to(gameCode).emit('team-buzzing', {
        teamId,
        teamName: team.name
      });

      // Отправляем обновление игры чтобы у ведущего появились кнопки
      sendGameUpdate(io, gameCode, game);
    });

    // Проверка ответа
    socket.on('submit-answer', async ({ gameCode, teamId, isCorrect }) => {
      const game = activeGames.get(gameCode);

      console.log(`📝 Submit answer: gameCode=${gameCode}, teamId=${teamId}, isCorrect=${isCorrect}`);

      if (!game || !game.currentQuestion) {
        console.log('⚠️  No game or no current question');
        return;
      }

      const team = game.teams.find(t => t.id === teamId);
      const basePoints = game.currentQuestion.points;
      // Удваиваем баллы во втором раунде
      const points = game.currentRound === 2 ? basePoints * 2 : basePoints;

      console.log(`Team: ${team?.name}, Base Points: ${basePoints}, Actual Points (Round ${game.currentRound}): ${points}`);

      // Найдем answerMedia из вопроса
      const category = game.selectedCategories.find(cat => cat.name === game.currentQuestion.category);
      const questionData = category?.questions.find(q => q.points === game.currentQuestion.points);
      const answerMedia = questionData?.answerMedia;

      if (isCorrect) {
        console.log('✅ Correct answer!');
        team.score += points;
        game.usedQuestions.push(game.currentQuestion);
        game.currentQuestion = null;
        game.answeringTeam = null;

        // Проверяем, закончилась ли игра
        const questionsPath = join(__dirname, '../data/questions.json');
        const questionsData = JSON.parse(readFileSync(questionsPath, 'utf-8'));
        const isGameFinished = checkGameFinished(game, questionsData.categories);

        if (isGameFinished) {
          console.log(`🏁 All questions answered and no more categories.`);

          // Если есть answerMedia, отправляем флаг shouldFinish вместо немедленного завершения
          if (answerMedia) {
            console.log('Answer media exists, delaying game finish');
            // Обновляем в БД (но не меняем статус на finished)
            try {
              await Game.findOneAndUpdate(
                { gameCode },
                {
                  teams: game.teams,
                  usedQuestions: game.usedQuestions,
                  currentQuestion: null,
                  answeringTeam: null
                }
              );
            } catch (error) {
              console.log('⚠️  Error updating in DB:', error.message);
            }

            io.to(gameCode).emit('answer-result', {
              teamId,
              isCorrect: true,
              newScore: team.score,
              questionClosed: true,
              answerMedia: answerMedia,
              shouldFinish: true
            });

            sendGameUpdate(io, gameCode, game);
            return;
          }

          // Если нет answerMedia, завершаем игру сразу
          game.status = 'finished';

          // Обновляем в БД
          try {
            await Game.findOneAndUpdate(
              { gameCode },
              {
                teams: game.teams,
                usedQuestions: game.usedQuestions,
                currentQuestion: null,
                answeringTeam: null,
                status: 'finished'
              }
            );
          } catch (error) {
            console.log('⚠️  Error finishing game in DB:', error.message);
          }

          io.to(gameCode).emit('answer-result', {
            teamId,
            isCorrect: true,
            newScore: team.score,
            questionClosed: true,
            answerMedia: answerMedia
          });

          io.to(gameCode).emit('game-finished', game);
          sendGameUpdate(io, gameCode, game);
          return;
        }

        // Обновляем в БД
        try {
          await Game.findOneAndUpdate(
            { gameCode },
            {
              teams: game.teams,
              usedQuestions: game.usedQuestions,
              currentQuestion: null,
              answeringTeam: null
            }
          );
        } catch (error) {
          console.log('⚠️  Error updating answer in DB:', error.message);
        }

        console.log('Sending answer-result (correct) and game-updated');
        io.to(gameCode).emit('answer-result', {
          teamId,
          isCorrect: true,
          newScore: team.score,
          questionClosed: true,
          answerMedia: answerMedia
        });
      } else {
        console.log('❌ Incorrect answer!');
        team.score -= points;

        // Добавляем команду в список ответивших
        if (!game.answeredTeams) {
          game.answeredTeams = [];
        }
        game.answeredTeams.push(teamId);

        game.answeringTeam = null;

        // Обновляем в БД
        try {
          await Game.findOneAndUpdate(
            { gameCode },
            { teams: game.teams, answeringTeam: null }
          );
        } catch (error) {
          console.log('⚠️  Error updating answer in DB:', error.message);
        }

        console.log('Sending answer-result (incorrect)');
        io.to(gameCode).emit('answer-result', {
          teamId,
          isCorrect: false,
          newScore: team.score,
          questionClosed: false
        });

        // Проверяем, остались ли команды которые могут ответить
        const remainingTeams = game.teams.filter(t => !game.answeredTeams.includes(t.id));
        console.log(`Remaining teams: ${remainingTeams.length}, Timer: ${game.timer}`);

        if (remainingTeams.length === 0 || game.timer <= 0) {
          console.log('No more teams or time up - closing question');
          // Все команды ответили неправильно или время вышло
          game.usedQuestions.push(game.currentQuestion);
          game.currentQuestion = null;
          game.answeredTeams = [];

          // Проверяем, закончилась ли игра
          const questionsPath = join(__dirname, '../data/questions.json');
          const questionsData = JSON.parse(readFileSync(questionsPath, 'utf-8'));
          const isGameFinished = checkGameFinished(game, questionsData.categories);

          if (isGameFinished) {
            console.log(`🏁 All questions answered and no more categories. Finishing game ${gameCode}`);
            game.status = 'finished';

            try {
              await Game.findOneAndUpdate(
                { gameCode },
                {
                  usedQuestions: game.usedQuestions,
                  currentQuestion: null,
                  answeredTeams: [],
                  status: 'finished'
                }
              );
            } catch (error) {
              console.log('⚠️  Error finishing game in DB:', error.message);
            }

            io.to(gameCode).emit('time-up');
            io.to(gameCode).emit('game-finished', game);
            sendGameUpdate(io, gameCode, game);
            return;
          }

          io.to(gameCode).emit('time-up');
          sendGameUpdate(io, gameCode, game);
        } else {
          console.log('Resuming timer for other teams');
          // Возобновляем таймер для других команд
          const timerInterval = setInterval(() => {
            game.timer--;
            io.to(gameCode).emit('timer-update', { timer: game.timer });

            if (game.timer <= 0) {
              clearInterval(timerInterval);
              io.to(gameCode).emit('time-up');
              game.currentQuestion = null;
              game.answeredTeams = [];
            }
          }, 1000);

          game.timerInterval = timerInterval;
        }
      }

      sendGameUpdate(io, gameCode, game);
    });

    // Досрочное закрытие вопроса ведущим
    socket.on('close-question', async ({ gameCode }) => {
      const game = activeGames.get(gameCode);

      if (!game || !game.currentQuestion) return;

      // Найдем answerMedia из вопроса перед закрытием
      const category = game.selectedCategories.find(cat => cat.name === game.currentQuestion.category);
      const questionData = category?.questions.find(q => q.points === game.currentQuestion.points);
      const answerMedia = questionData?.answerMedia;

      // Останавливаем таймер
      if (game.timerInterval) {
        clearInterval(game.timerInterval);
      }

      // Добавляем вопрос в использованные (чтобы он пропал с доски)
      game.usedQuestions.push(game.currentQuestion);

      // Закрываем вопрос
      game.currentQuestion = null;
      game.answeringTeam = null;
      game.timer = null;

      // Проверяем, закончилась ли игра
      const questionsPath = join(__dirname, '../data/questions.json');
      const questionsData = JSON.parse(readFileSync(questionsPath, 'utf-8'));
      const isGameFinished = checkGameFinished(game, questionsData.categories);

      if (isGameFinished) {
        console.log(`🏁 All questions answered and no more categories.`);

        // Если есть answerMedia, отправляем флаг shouldFinish вместо немедленного завершения
        if (answerMedia) {
          console.log('Answer media exists, delaying game finish');
          try {
            await Game.findOneAndUpdate(
              { gameCode },
              {
                usedQuestions: game.usedQuestions,
                currentQuestion: null,
                answeringTeam: null
              }
            );
          } catch (error) {
            console.log('⚠️  Error updating in DB:', error.message);
          }

          io.to(gameCode).emit('time-up', { answerMedia: answerMedia, shouldFinish: true });
          sendGameUpdate(io, gameCode, game);
          return;
        }

        // Если нет answerMedia, завершаем игру сразу
        game.status = 'finished';

        try {
          await Game.findOneAndUpdate(
            { gameCode },
            {
              usedQuestions: game.usedQuestions,
              currentQuestion: null,
              answeringTeam: null,
              status: 'finished'
            }
          );
        } catch (error) {
          console.log('⚠️  Error finishing game in DB:', error.message);
        }

        io.to(gameCode).emit('time-up', { answerMedia: answerMedia });
        io.to(gameCode).emit('game-finished', game);
        sendGameUpdate(io, gameCode, game);
        return;
      }

      // Обновляем в БД
      try {
        await Game.findOneAndUpdate(
          { gameCode },
          {
            usedQuestions: game.usedQuestions,
            currentQuestion: null,
            answeringTeam: null
          }
        );
      } catch (error) {
        console.log('⚠️  Error updating close-question in DB:', error.message);
      }

      io.to(gameCode).emit('time-up', { answerMedia: answerMedia });
      sendGameUpdate(io, gameCode, game);
    });

    // Завершение игры
    socket.on('finish-game', async ({ gameCode }) => {
      const game = activeGames.get(gameCode);

      if (!game) return;

      game.status = 'finished';

      // Обновляем в БД
      try {
        await Game.findOneAndUpdate({ gameCode }, { status: 'finished' });
      } catch (error) {
        console.log('⚠️  Error updating game status in DB:', error.message);
      }

      io.to(gameCode).emit('game-finished', game);
      sendGameUpdate(io, gameCode, game);

      console.log(`🏁 Game ${gameCode} finished`);
    });

    // Переход к следующему раунду
    socket.on('next-round', async ({ gameCode }) => {
      const game = activeGames.get(gameCode);

      if (!game) return;

      // Загружаем все категории
      const questionsPath = join(__dirname, '../data/questions.json');
      const questionsData = JSON.parse(readFileSync(questionsPath, 'utf-8'));

      // Выбираем 5 новых случайных категорий (исключая использованные)
      const selectedCategories = selectRandomCategories(
        questionsData.categories,
        game.usedCategoryIds
      );

      // Проверяем, есть ли еще доступные категории
      if (selectedCategories.length === 0) {
        // Категории закончились - завершаем игру автоматически
        console.log(`🏁 No more categories available. Finishing game ${gameCode}`);
        game.status = 'finished';

        try {
          await Game.findOneAndUpdate(
            { gameCode },
            { status: 'finished' }
          );
        } catch (error) {
          console.log('⚠️  Error finishing game in DB:', error.message);
        }

        io.to(gameCode).emit('game-finished', game);
        sendGameUpdate(io, gameCode, game);
        return;
      }

      // Увеличиваем номер раунда
      const nextRound = game.currentRound + 1;

      game.currentRound = nextRound;
      game.selectedCategories = selectedCategories;
      game.usedCategoryIds = [...game.usedCategoryIds, ...selectedCategories.map(cat => cat.id)];
      game.usedQuestions = [];
      game.currentQuestion = null;

      // Обновляем в БД
      try {
        await Game.findOneAndUpdate(
          { gameCode },
          {
            currentRound: nextRound,
            selectedCategories: selectedCategories,
            usedCategoryIds: game.usedCategoryIds,
            usedQuestions: [],
            currentQuestion: null
          }
        );
      } catch (error) {
        console.log('⚠️  Error updating round in DB:', error.message);
      }

      console.log(`🎮 Game ${gameCode}: Starting round ${nextRound} with ${selectedCategories.length} categories`);
      io.to(gameCode).emit('round-changed', { round: nextRound });
      sendGameUpdate(io, gameCode, game);
    });

    // Отключение
    socket.on('disconnect', () => {
      console.log('👤 Client disconnected:', socket.id);

      // Удаляем игрока из всех игр
      activeGames.forEach((game, gameCode) => {
        // Проверяем, является ли отключившийся хостом
        const isHost = game.hostId === socket.id;

        if (isHost) {
          // Если отключился хост, игра завершается
          activeGames.delete(gameCode);
          io.to(gameCode).emit('host-disconnected');
          console.log(`🗑️  Game ${gameCode} deleted (host disconnected)`);
          return;
        }

        // Удаляем игрока из команд
        game.teams.forEach(team => {
          team.players = team.players.filter(p => p.id !== socket.id);
        });

        // Удаляем пустые команды
        game.teams = game.teams.filter(team => team.players.length > 0);

        // Обновляем игру для оставшихся игроков
        sendGameUpdate(io, gameCode, game);
      });
    });
  });
};
