import { QuizAnswerStateEnum } from './quiz-answer-state.enum';
import {CommandBus, EventBus, ofType, QueryBus} from '@nestjs/cqrs';
import {Injectable, Logger} from '@nestjs/common';
import { IncreasePlayerWinningRateCommand } from '../commands/increase-player-winning-rate.command';
import { CreateNewQueueItemCommand } from '../commands/create-new-queue-item.command';
import { GameQueueTypes } from '../../schemas/game-queue.schema';
import { NotifyCardOnScreenCommand } from '../commands/notify-card-on-screen-command';
import { SendToastCommand } from '../commands/send-toast.command';
import { getRandomInt } from '../../helpers/rand-number';
import { GiveOutAPrizeCommand } from '../commands/give-out-a-prize.command';
import {SelectTargetPlayerCommand} from "../commands/select-target-player.command";

import {DebuffsConsts} from "./debuffs.consts";
import {NextQuestionEvent} from "../events/next-question.event";
import {FilterGuestsWithPropertyQuery} from "../../guests/queries/filterguestswithproperty.query";
import {DebuffCardPlayedEvent} from "../events/debuff-card-played.event";
import {filter} from "rxjs";
import {SetGuestPropertyCommand} from "../../guests/command/set-guest-property.command";
import {StringHelper} from "../../helpers/stringhelper";
import {GetGuestQuery} from "../../guests/queries/getguest.query";
import {CardsSetChangedEvent} from "../events/cards-events/cards-set-changed.event";
import {GuestPropertiesConsts} from "../../schemas/properties.consts";
import {BeginVersusCommand} from "../commands/begin-versus.command";
import {CheckIfAnotherVersusInProgressQuery} from "../queries/check-if-another-versus-in-progress.query";

export interface IGameCard {
  setupHandlers(eventBus: EventBus, commandBus: CommandBus, queryBus: QueryBus): void;
}

export class GameCard implements IGameCard{
  protected readonly logger = new Logger(GameCard.name);
  protected commandBus: CommandBus;
  protected eventBus: EventBus;
  protected queryBus: QueryBus;
  protected telegramId: number;
  protected destinationTelegramId: number;
  public handlers:  { event: any, commands: any[]}[] = [];
  name: string;
  description: string;
  protected emoji: string;
  public dealOnStart = false;
  chance = 0;
  getEmoji() {
    return this.emoji;
  }

  setup(eb: EventBus, cb: CommandBus, qb: QueryBus, tId: number, destTId: number) {
    this.eventBus = eb;
    this.commandBus = cb;
    this.telegramId = tId;
    this.destinationTelegramId = destTId;
    this.queryBus = qb;

  }
  async handle() {
    this.logger.verbose(`Card has no handler`);
  }
  mightBePlayed: QuizAnswerStateEnum;

  setupHandlers(eventBus: EventBus, commandBus: CommandBus, queryBus: QueryBus) {
  }

}

export class DoubleTreasureCard extends GameCard {
  name = DoubleTreasureCard.name;
  description = 'Удвоить приз';
  chance = 4;
  emoji = '💰';
  mightBePlayed = QuizAnswerStateEnum.playerGettingReward;
  async handle(): Promise<void> {
    await this.commandBus.execute(
      new NotifyCardOnScreenCommand(this.telegramId, this),
    );
    await this.commandBus.execute(
      new GiveOutAPrizeCommand(this.telegramId),
    );
    const userSrc = await this.queryBus.execute(new GetGuestQuery(this.telegramId));
    const subjcaseFrom = userSrc.get(StringHelper.getPropertyName(GuestPropertiesConsts.NameSubjectiveCase));
    const message = `${subjcaseFrom} решает удвоить приз!`;
    await this.commandBus.execute(new SendToastCommand(message, 8000));
  }
}

export class StolePrizeCard extends GameCard {
  name = StolePrizeCard.name;
  description = 'Украсть приз';
  chance = 15;
  emoji = '🥷🏽';
  mightBePlayed = QuizAnswerStateEnum.someoneGettingReward;
  async handle(): Promise<void> {
    this.logger.verbose(`StolePrize from player ${this.telegramId} to player ${this.destinationTelegramId}`);
    await this.commandBus.execute(
      new NotifyCardOnScreenCommand(this.telegramId, this),
    );
    const userSrc = await this.queryBus.execute(new GetGuestQuery(this.telegramId));
    const userDst = await this.queryBus.execute(new GetGuestQuery(this.destinationTelegramId));
    const subjcaseFrom = userSrc.get(StringHelper.getPropertyName(GuestPropertiesConsts.NameSubjectiveCase));
    const genitiveTo = userDst.get(StringHelper.getPropertyName(GuestPropertiesConsts.NameGenitiveCase));
    const message = `${subjcaseFrom} крадет приз у  ${genitiveTo}`;
    await this.commandBus.execute(new SendToastCommand(message, 8000));
  }
}

export class ShitCard extends GameCard {
  name = ShitCard.name;
  description = 'Говнокарта';
  chance = 50;
  emoji = '💩';
  mightBePlayed = QuizAnswerStateEnum.someoneAnsweredCorrectly;

  async handle(): Promise<void> {
    this.logger.verbose(`Shit card from player ${this.telegramId} to player ${this.destinationTelegramId}`);
    await this.commandBus.execute(
      new NotifyCardOnScreenCommand(this.telegramId, this),
    );
    await this.commandBus.execute(
      new CreateNewQueueItemCommand(
        this.destinationTelegramId,
        GameQueueTypes.additionalQuestion,
      ),
    );
    const userSrc = await this.queryBus.execute(new GetGuestQuery(this.telegramId));
    const userDst = await this.queryBus.execute(new GetGuestQuery(this.destinationTelegramId));
    const subjcaseFrom = userSrc.get(StringHelper.getPropertyName(GuestPropertiesConsts.NameSubjectiveCase));
    const accusativeTo = userDst.get(StringHelper.getPropertyName(GuestPropertiesConsts.NameAccusativeCase));
    const message = `${subjcaseFrom} кидает говно-карту в ${accusativeTo}`;
    await this.commandBus.execute(new SendToastCommand(message, 8000));
  }
}

export class LuckyCard extends GameCard {
  name = LuckyCard.name;
  description = 'Лаки-карта';
  chance = 32;
  mightBePlayed = QuizAnswerStateEnum.playerAnsweredCorrectly;
  emoji = '🍀';
  async handle() {
    this.logger.verbose(`Handling card`);
    const user = await this.queryBus.execute(new GetGuestQuery(this.telegramId));
    const subjcase = user.get(StringHelper.getPropertyName(GuestPropertiesConsts.NameSubjectiveCase));
    const message = `${subjcase} использует лаки карту`;
    this.logger.verbose(`message: ${message}`);
    await this.commandBus.execute(new SendToastCommand(message, 8000));
    await this.commandBus.execute(
      new NotifyCardOnScreenCommand(this.telegramId, this),
    );
    await this.commandBus.execute(
      new IncreasePlayerWinningRateCommand(
        this.telegramId,
        getRandomInt(8, 20),
      ),
    );

  }
}

export class AvoidPenaltyCard extends GameCard {
  dealOnStart = true;
  name = AvoidPenaltyCard.name;
  description = 'Избежать наказание';
  chance = 25;
  emoji = '🏃‍';
  mightBePlayed = QuizAnswerStateEnum.playerGotPenalty;
  async handle() {
    const user = await this.queryBus.execute(new GetGuestQuery(this.telegramId));
    const subjcase = user.get(StringHelper.getPropertyName(GuestPropertiesConsts.NameSubjectiveCase));
    const message = `${subjcase} избегает наказания`;
    this.logger.verbose(`message: ${message}`);
    await this.commandBus.execute(new SendToastCommand(message, 8000));
    await this.commandBus.execute(
      new NotifyCardOnScreenCommand(this.telegramId, this),
    );


  }
}

@Injectable()
export class VersusCard extends GameCard {
  dealOnStart = true;
  name = VersusCard.name;
  chance = 10;
  emoji = '🆚';
  description = 'Поединок';
  mightBePlayed = QuizAnswerStateEnum.betweenRounds;
  async setupHandlers(eventBus: EventBus, commandBus: CommandBus, queryBus: QueryBus) {
    super.setupHandlers(eventBus, commandBus, queryBus);
    eventBus.pipe(
      ofType(DebuffCardPlayedEvent),
      filter(x => x.debufName == DebuffsConsts.versus))
      .subscribe(async (r) =>{
        const versusInProgress = await queryBus.execute(new CheckIfAnotherVersusInProgressQuery());
        this.logger.verbose(`versusInProgress ${versusInProgress}`);
        if(versusInProgress) {
          this.logger.warn(`another versus in progress`);
          return;
        }
        const destUser = await queryBus.execute(new GetGuestQuery(r.dest))
        const sourceUser = await queryBus.execute(new GetGuestQuery(r.from));
        await commandBus.execute(new BeginVersusCommand(sourceUser.telegramId, destUser.telegramId));
    });
  }
  async handle() {
    await this.commandBus.execute(
      new SelectTargetPlayerCommand(this.telegramId, DebuffsConsts.versus, 0, false)
    )
    await this.queryBus.execute(new FilterGuestsWithPropertyQuery(null,null,null));
    this.eventBus.subscribe((data) =>{
      this.logger.verbose(`Response from cmdBus: ${data}`);
    });
  }
}

@Injectable()
export class BanPlayer extends GameCard {
  dealOnStart = true;
  name =  BanPlayer.name;
  description = 'Забанить игрока';
  chance = 10;
  emoji = '🚷';
  mightBePlayed = QuizAnswerStateEnum.betweenRounds;

  async setupHandlers(eventBus: EventBus, commandBus: CommandBus, queryBus: QueryBus) {
    super.setupHandlers(eventBus, commandBus, queryBus);
    eventBus.pipe(ofType(DebuffCardPlayedEvent),filter(x => x.debufName == DebuffsConsts.bannedFor)).subscribe(async (r) => {
      const destUser = await queryBus.execute(new GetGuestQuery(r.dest))
      const sourceUser = await queryBus.execute(new GetGuestQuery(r.from));
      const subjectiveSource = sourceUser.get(StringHelper.getPropertyName(GuestPropertiesConsts.NameSubjectiveCase));
      const accusativeDest = destUser.get(StringHelper.getPropertyName(GuestPropertiesConsts.NameAccusativeCase));
      const message = `Игрок ${subjectiveSource} забанил ${accusativeDest} `;
      await commandBus.execute(new SetGuestPropertyCommand(r.dest, DebuffsConsts.bannedFor, r.data, true));
      await commandBus.execute(new SendToastCommand(message, 8000));
      await commandBus.execute(new NotifyCardOnScreenCommand(sourceUser.telegramId, this,0 ));
      eventBus.publish(new CardsSetChangedEvent(sourceUser.telegramId));
    })
    eventBus.pipe(ofType(NextQuestionEvent)).subscribe(async (r)=> {
      const players = await queryBus.execute(new FilterGuestsWithPropertyQuery(DebuffsConsts.bannedFor, '$gt', 0))
      this.logger.verbose(`enter: ban card handler, banned players count ${players.length}`);
      players.map(async (player) => {
        const newBannedFor = parseInt(player.get(StringHelper.getPropertyName(DebuffsConsts.bannedFor))) - 1;
        await commandBus.execute(
          new SetGuestPropertyCommand(
            player.telegramId, DebuffsConsts.bannedFor,
            newBannedFor.toString()
          ));
      });
    })
  }

  async handle() {
    await this.commandBus.execute(
      new SelectTargetPlayerCommand(this.telegramId, DebuffsConsts.bannedFor, getRandomInt(2,3), false)
    )
    await this.queryBus.execute(new FilterGuestsWithPropertyQuery(null,null,null));
    this.eventBus.subscribe((data) =>{
      this.logger.verbose(`Response from cmdBus: ${data}`);
    });
  }
}

export const gameCards: typeof GameCard[] = [
  DoubleTreasureCard,
  StolePrizeCard,
 // ShitCard,
  LuckyCard,
  AvoidPenaltyCard,
  BanPlayer,
  VersusCard,
];
