import { pool } from '../config/db';
import { GameState } from '../models/GameState';
import { Question } from '../models/Question';
import { createLogger } from '../utils/logger';

// Service for handling game state
class GameService {
  logger = createLogger('GameService');

  public async getLobbyState(code: string): Promise<GameState> {
    try {
      const result = await pool.query<GameState>('SELECT state FROM lobby WHERE code = $1', [code]);
      if (result.rows.length === 0) {
        throw new Error(`Lobby with code ${code} not found`);
      }
      return result.rows[0].state as unknown as GameState;
    } catch (err) {
      this.logger.error(`Error fetching state for lobby ${code}:`, err);
      throw new Error('Failed to fetch lobby state');
    }
  }

  public async updateLobbyState(code: string, newState: GameState): Promise<void> {
    try {
      await pool.query('UPDATE lobby SET state = $1 WHERE code = $2', [
        JSON.stringify(newState),
        code,
      ]);
      this.logger.info(`State updated for lobby ${code}:`, newState);
    } catch (err) {
      this.logger.error(`Error updating state for lobby ${code}:`, err);
      throw new Error('Failed to update lobby state');
    }
  }

  async getRandomQuestion(tags?: string[] | null): Promise<Question> {
    let query = `
      SELECT id, question, answer, picture, tag
      FROM question
    `;

    // If tags array is provided and not empty, filter by them
    if (tags && tags.length > 0) {
      query += ` WHERE tag = ANY($1)`;
    }
    
    query += ` ORDER BY RANDOM() LIMIT 1`;
    let result;
    
    if (tags && Array.isArray(tags) && tags.length > 0) {
      result = await pool.query<Question>(query, [tags]);
      this.logger.info(`Fetching question with tags: ${tags.join(', ')}`);
    } else {
      result = await pool.query<Question>(query);
      this.logger.info(`Fetching question with no tag filter`);
    }
    
    if (result.rows.length === 0) {

      // If no questions with the specified tags are found, get a random question without tag filtering
      this.logger.info(`No questions found with tags: ${tags}. Getting a random question.`);
      result = await pool.query<Question>(`
        SELECT id, question, answer, picture 
        FROM question 
        ORDER BY RANDOM() 
        LIMIT 1
      `);
      
      if (result.rows.length === 0) {
        throw new Error('No questions found in the database');
      }
    }
    
    return result.rows[0];
  }

  // 2. Validate a question
  public async validateQuestion(questionId: number, answer: string): Promise<boolean> {
    try {
      const result = await pool.query<Question>('SELECT * FROM questions WHERE id = $1', [
        questionId,
      ]);
      if (result.rows.length === 0) {
        this.logger.warn(`Question with ID ${questionId} not found`);
        return false;
      }

      const question = result.rows[0];
      const isValid = question.answer.some((correctAnswer: string) => 
        correctAnswer.toLowerCase() === answer.toLowerCase()
      );
      this.logger.info(`Validation result for question ${questionId}: ${isValid}`);
      return isValid;
    } catch (err) {
      this.logger.error(`Error validating question ${questionId}:`, err);
      throw new Error('Failed to validate question');
    }
  }

  public calculatePoints(currentTimer: number, secondsPerRound: number): number {
    // Ensure currentTimer is within the valid range
    if (currentTimer < 0) currentTimer = 0;
    if (currentTimer > secondsPerRound) currentTimer = secondsPerRound;

    // Calculate the points based on the time left
    const maxPoints = 10;
    const minPoints = 1;

    const points = Math.round(
      minPoints + ((maxPoints - minPoints) * currentTimer) / secondsPerRound
    );

    return points;
  }

  public async updatePointsForPlayer(
    code: string,
    userId: number,
    points: number
  ): Promise<number> {
    try {
      const result = await pool.query(
        `
        UPDATE lobby_players
        SET score = score + $1
        WHERE lobby = $2 AND player = $3
        RETURNING score
        `,
        [points, code, userId]
      );

      if (result.rowCount === 0) {
        throw new Error(`Player with ID ${userId} not found in lobby ${code}`);
      }

      const newScore = result.rows[0].score;

      this.logger.info(
        `Updated score for player ${userId} in lobby ${code}. Points added: ${points}. New score: ${newScore}`
      );

      return newScore;
    } catch (err) {
      this.logger.error(`Error updating points for player ${userId} in lobby ${code}:`, err);
      throw new Error('Failed to update points for player');
    }
  }
}

export default new GameService();
