import {
  Game,
  type GamePlayer,
  type IGameConfig,
  type IGameState,
} from "@/game-engien";

interface QuizQuestion {
  id: string;
  text: string;
  options: string[];
  correctOptionIndex: number;
  imageUrl?: string;
  timeLimit?: number;
  points: number;
}

export interface QuizData {
  version: string;
  id: string;
  title: string;
  description: string;
  author: string;
  questions: Array<QuizQuestion>;
  category: string;
  difficulty: "easy" | "medium" | "hard";
  tags: Array<string>;
}

interface QuizGameConfig extends IGameConfig {
  randomizeQuestions?: boolean;
  revealAnswersImmediately?: boolean;
  pointsPerQuestion?: number;
}

interface QuizGameState extends IGameState {
  currentQuestion?: QuizQuestion;
  answeredQuestions: Set<string>;
  playerAnswers: Record<string, Record<string, number>>;
  leaderboard: Array<GamePlayer>;
}

type PlayerQuizAction =
  | {
      type: "answer";
      questionId: string;
      selectedOptionIndex: number;
    }
  | {
      type: "ready";
    }
  | {
      type: "requestHint";
      questionId: string;
    };

export class QuizGame extends Game {
  private quizData: QuizData;
  private questions: QuizQuestion[];
  private quizState: QuizGameState;
  private timers: Map<string, NodeJS.Timeout> = new Map();

  constructor(quizData: QuizData, config: QuizGameConfig = {}) {
    super(
      {
        name: "quiz",
        id: quizData.id,
        title: quizData.title,
        description: quizData.description,
        version: quizData.version,
      },
      {
        timeLimit: 15, // Default 15 seconds per question
        scoreMultiplier: 1,
        allowHints: false,
        ...config,
      },
    );

    this.quizData = quizData;
    this.questions = [...quizData.questions];
    this.quizState = {
      ...this.state,
      answeredQuestions: new Set<string>(),
      playerAnswers: {},
      leaderboard: [],
      totalRounds: this.questions.length,
    };
    this.state = this.quizState;
  }

  public initialize(config?: QuizGameConfig): void {
    // Override the default config with any provided config
    if (config) {
      this.config = {
        ...this.config,
        ...config,
      };
    }

    // Initialize player answers tracking
    this.players.forEach((player) => {
      this.quizState.playerAnswers[player.id] = {};
    });

    // Randomize questions if configured
    if ((this.config as QuizGameConfig).randomizeQuestions) {
      this.questions = this.shuffleArray([...this.questions]);
    }

    this.state.totalRounds = this.questions.length;
    this.state.status = "waiting";
  }

  public start(): void {
    if (this.state.status !== "waiting") {
      throw new Error("Game must be in waiting state to start");
    }

    this.state.status = "active";
    this.nextQuestion();
  }

  public end(): void {
    this.clearTimers();
    this.state.status = "completed";
    this.quizState.leaderboard = [...this.players].sort(
      (a, b) => b.score - a.score,
    );

    // Notify players of game end
    this.broadcastGameEnd();
  }

  public handlePlayerAction(playerId: string, action: PlayerQuizAction): void {
    const player = this.players.find((p) => p.id === playerId);

    if (!player) {
      throw new Error(`Player ${playerId} not found`);
    }

    switch (action.type) {
      case "answer":
        this.handlePlayerAnswer(
          player,
          action.questionId,
          action.selectedOptionIndex,
        );
        break;
      case "ready":
        this.handlePlayerReady(player);
        break;
      case "requestHint":
        this.handleRequestHint(player, action.questionId);
        break;
      default:
        throw new Error(`Unknown action type: ${(action as any).type}`);
    }
  }

  private handlePlayerAnswer(
    player: GamePlayer,
    questionId: string,
    selectedOptionIndex: number,
  ): void {
    const currentQuestion = this.quizState.currentQuestion;

    if (!currentQuestion || currentQuestion.id !== questionId) {
      throw new Error("Invalid question ID");
    }

    // Record the answer
    this.quizState.playerAnswers[player.id][questionId] = selectedOptionIndex;

    // Check if answer is correct and update score
    if (selectedOptionIndex === currentQuestion.correctOptionIndex) {
      const points =
        currentQuestion.points * (this.config.scoreMultiplier || 1);
      player.score += points;

      // Broadcast correct answer event
      this.broadcastPlayerCorrectAnswer(player, questionId, points);
    }

    // Check if all players have answered
    const allAnswered = this.players.every(
      (p) => questionId in (this.quizState.playerAnswers[p.id] || {}),
    );

    if (allAnswered) {
      this.clearTimers();

      // If configured to reveal answers immediately, wait briefly before next question
      if ((this.config as QuizGameConfig).revealAnswersImmediately) {
        this.broadcastQuestionResults(currentQuestion);
        setTimeout(() => this.nextQuestion(), 3000);
      } else {
        this.nextQuestion();
      }
    }
  }

  private handlePlayerReady(player: GamePlayer): void {
    // Could implement a ready-up system before starting each question
    // For now, just acknowledging the ready state
  }

  private handleRequestHint(player: GamePlayer, questionId: string): void {
    if (!this.config.allowHints) {
      return;
    }

    const currentQuestion = this.quizState.currentQuestion;

    if (!currentQuestion || currentQuestion.id !== questionId) {
      throw new Error("Invalid question ID");
    }

    // Implementation of hint system
    // For example, eliminate one wrong option
    this.provideHint(player, currentQuestion);
  }

  private nextQuestion(): void {
    if (this.state.status !== "active") {
      return;
    }

    this.clearTimers();

    // Check if we've gone through all questions
    if (this.state.currentRound >= this.questions.length) {
      this.end();
      return;
    }

    const nextQuestion = this.questions[this.state.currentRound];
    this.quizState.currentQuestion = nextQuestion;
    this.state.currentRound++;
    this.quizState.answeredQuestions.add(nextQuestion.id);

    // Set timer for question
    const timeLimit = nextQuestion.timeLimit || this.config.timeLimit || 15;
    this.state.timeRemaining = timeLimit;

    // Broadcast the new question to all players
    this.broadcastQuestion(nextQuestion);

    // Start the timer
    const timer = setInterval(() => {
      if (this.state.timeRemaining && this.state.timeRemaining > 0) {
        this.state.timeRemaining--;
      } else {
        this.clearTimers();
        this.timeExpired(nextQuestion);
      }
    }, 1000);

    this.timers.set("questionTimer", timer);
  }

  private timeExpired(question: QuizQuestion): void {
    // Handle when time expires for a question
    // Auto-submit blank answers for players who didn't answer
    this.players.forEach((player) => {
      if (!(question.id in (this.quizState.playerAnswers[player.id] || {}))) {
        this.quizState.playerAnswers[player.id][question.id] = -1; // -1 means no answer
      }
    });

    if ((this.config as QuizGameConfig).revealAnswersImmediately) {
      this.broadcastQuestionResults(question);
      setTimeout(() => this.nextQuestion(), 3000);
    } else {
      this.nextQuestion();
    }
  }

  private clearTimers(): void {
    this.timers.forEach((timer) => clearInterval(timer));
    this.timers.clear();
  }

  private shuffleArray<T>(array: T[]): T[] {
    const newArray = [...array];
    for (let i = newArray.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      [newArray[i], newArray[j]] = [newArray[j], newArray[i]];
    }
    return newArray;
  }

  private provideHint(player: GamePlayer, question: QuizQuestion): void {
    // Implementation of hint system
    // This is a placeholder for the hint logic

    // Example: Find a wrong answer to eliminate
    const wrongOptions = question.options
      .map((option, index) => ({ option, index }))
      .filter((item) => item.index !== question.correctOptionIndex);

    if (wrongOptions.length > 0) {
      const randomWrongOption =
        wrongOptions[Math.floor(Math.random() * wrongOptions.length)];
      this.broadcastHint(player, question.id, randomWrongOption.index);
    }
  }

  // Broadcasting methods (these would connect to your websocket/realtime system)
  private broadcastQuestion(question: QuizQuestion): void {
    // In a real implementation, this would send the question to all players
    console.log("Broadcasting question:", question.text);

    // Example of what to broadcast (would be sent via websocket)
    const broadcastData = {
      type: "question",
      questionId: question.id,
      text: question.text,
      options: question.options,
      imageUrl: question.imageUrl,
      timeLimit: question.timeLimit || this.config.timeLimit,
      points: question.points,
    };

    // Send to all players
    // this.broadcastToPlayers(broadcastData);
  }

  private broadcastQuestionResults(question: QuizQuestion): void {
    // In a real implementation, this would send the results to all players
    console.log("Broadcasting question results for:", question.text);

    // Example of what to broadcast
    const broadcastData = {
      type: "questionResults",
      questionId: question.id,
      correctOptionIndex: question.correctOptionIndex,
      playerAnswers: this.quizState.playerAnswers,
    };

    // Send to all players
    // this.broadcastToPlayers(broadcastData);
  }

  private broadcastPlayerCorrectAnswer(
    player: GamePlayer,
    questionId: string,
    points: number,
  ): void {
    // Example of what to broadcast when a player answers correctly
    const broadcastData = {
      type: "correctAnswer",
      playerId: player.id,
      playerName: player.displayName,
      questionId: questionId,
      points: points,
    };

    // Send to all players
    // this.broadcastToPlayers(broadcastData);
  }

  private broadcastGameEnd(): void {
    // Example of what to broadcast when the game ends
    const broadcastData = {
      type: "gameEnd",
      leaderboard: this.quizState.leaderboard,
    };

    // Send to all players
    // this.broadcastToPlayers(broadcastData);
  }

  private broadcastHint(
    player: GamePlayer,
    questionId: string,
    eliminatedOptionIndex: number,
  ): void {
    // Example of what to broadcast when providing a hint
    const broadcastData = {
      type: "hint",
      playerId: player.id,
      questionId: questionId,
      eliminatedOptionIndex: eliminatedOptionIndex,
    };

    // Send to specific player
    // this.sendToPlayer(player.id, broadcastData);
  }
}

// Game Registry to manage different games
class GameRegistry {
  private games: Map<string, any> = new Map();

  registerGame(gameClass: any): void {
    const tempInstance = new gameClass({} as any);
    this.games.set(tempInstance.getGameId(), gameClass);
  }

  createGameInstance(gameId: string, ...args: any[]): Game {
    const GameClass = this.games.get(gameId);

    if (!GameClass) {
      throw new Error(`Game ${gameId} not found in registry`);
    }

    return new GameClass(...args);
  }

  listAvailableGames(): string[] {
    return Array.from(this.games.keys());
  }
}
