import {Injectable, InternalServerErrorException, Logger, OnApplicationBootstrap} from '@nestjs/common';
import {CommandBus, EventBus, QueryBus} from '@nestjs/cqrs';
import {CardSelectionTimeExceedCommand} from './commands/card-selection-time-exceed.command';
import {InjectModel} from '@nestjs/mongoose';
import {GameQueue, GameQueueDocument, GameQueueTypes,} from '../schemas/game-queue.schema';
import {Model, Promise} from 'mongoose';
import {ProceedGameQueueCommand} from './commands/proceed-game-queue.command';
import {SharedService} from '../shared/shared.service';
import {ConfigService} from "@nestjs/config";
import {gameCards} from "./entities/cards.entities";
import {IEmptyNotification} from "../Consts/types";
import {ClientNotificationType} from "../socket/socket.gateway";
import {GetGuestQuery} from "../guests/queries/getguest.query";
import {ValidAnswerReceivedEvent} from "./events/valid-answer.recieved";

@Injectable()
export class GameService implements OnApplicationBootstrap{

  private readonly logger = new Logger(GameService.name);
  constructor(
    private cmdBus: CommandBus,
    private eventBus: EventBus,
    @InjectModel(GameQueue.name) private gameQueueModel: Model<GameQueueDocument>,
    private configService: ConfigService,
    private sharedService: SharedService,
    private commandBus: CommandBus,
    private queryBus: QueryBus,
  ) {

  }
  public beginCardSelectionScene() {
    const delay = parseInt(this.configService.get('SCENE_DELAY'));
    //const delay = 7;
    setTimeout(async () => {
      await this.cmdBus.execute(new CardSelectionTimeExceedCommand());
    }, delay * 1000);
  }

  async addTaskToGameQueue(target: number, type: GameQueueTypes, text: string|null = null) {
    this.logger.verbose(`Adding to game queue ${type} for player ${target}`)
    const q = new this.gameQueueModel({
      type: type,
      target: target,
      text: text ? text : '',
    });
    await q.save();
  }

  async cleanGameQueue() {
    this.logger.verbose(`Cleaning game queue`);
    await this.gameQueueModel.deleteMany({});
  }

  async getGameQueueItem() {
    const item = await this.gameQueueModel.aggregate([
      {
        $match: { completed: false }
      },
      {
        $addFields: {
          priority: {
            $cond: [{ $eq: ["$type", "versus"] }, 1, 0]
          }
        }
      },
      {
        $sort: { priority: -1 }
      },
      {
        $limit: 1
      }
    ]).exec();
    console.log(item[0]);
    return item[0];
  }

  async markQueueAsCompleted(id: string| null) {
    let qItem;
    if(!id) {
      qItem = await this.gameQueueModel.findOne({ completed: false }).exec();
    } else {
      qItem = await this.gameQueueModel.findById(id).exec();
    }
    this.logger.verbose(`Set ${qItem.id} in queue as completed`);
    if (!qItem) {
      throw new InternalServerErrorException('no such item');

    }
    qItem.completed = true;
    await qItem.save();
    this.sharedService.notifyAllClients<IEmptyNotification>(ClientNotificationType.QueueCompleted, {});
    await this.cmdBus.execute(new ProceedGameQueueCommand());
    return qItem;
  }

  async pauseGame() {
    await this.sharedService.setConfig('game_state', 'paused');
    this.sharedService.notifyAllClients<IEmptyNotification>(ClientNotificationType.GamePaused, {});
    return Promise.resolve({ result: true });
  }

  async resumeGame() {
    await this.sharedService.setConfig('game_state', 'running');
    this.sharedService.notifyAllClients<IEmptyNotification>(ClientNotificationType.GameResumed,{});
    return Promise.resolve({ result: true });
  }

  getState() {
    return this.sharedService.getConfig('game_state');
  }

  async playExtraCards() {
    this.logger.verbose(`playExtraCards entering`);
    //await this.cmdBus.execute(new SendBetweenRoundsActionsCommand());
  }

  onApplicationBootstrap(): any {
    this.logger.verbose("Initialize cards handler");
    gameCards.map((card) => {
      const cardInstance = new card();
      this.logger.verbose(`Adding handler for ${cardInstance.name}`);
      cardInstance.setupHandlers(this.eventBus, this.commandBus, this.queryBus);
    })
  }

  async getStateDetails() {
    return await this.sharedService.getConfig('current_action') || null;
  }

  async clearGameQueue() {
    await this.gameQueueModel.deleteMany({}).exec();
    return { result: true };
  }

  async simulateValidAnswer() {
    this.eventBus.publish(new ValidAnswerReceivedEvent(11178819, 'test', ''));
  }
}
