import { Injectable, HttpException, HttpStatus } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, Not } from 'typeorm';

import { FactionEntity, GameGroupEntity } from './faction.entity';
import {
  JoinFactionDTO,
  GameGroupDTO,
  JoinGameGroupDTO,
  FactionDTO,
} from './faction.dto';
import { Game_PersonEntity } from '../game/game.entity';

/*
FactionService contains functions for
- Joining faction
- Leaving faction
- Change faction multiplier (not implemented)

Group
- Creating group
- List faction players in groups

Player
- Promote player
- verifying user
*/

@Injectable()
export class FactionService {
  constructor(
    @InjectRepository(FactionEntity)
    private factionRepository: Repository<FactionEntity>,
    @InjectRepository(Game_PersonEntity)
    private game_PersonRepository: Repository<Game_PersonEntity>,
    @InjectRepository(GameGroupEntity)
    private game_GroupRepository: Repository<GameGroupEntity>,
  ) {}

  async joinFaction(person, faction: JoinFactionDTO) {
    // get faction
    const factionInDb = await this.factionRepository.findOne({
      factionId: faction.factionId,
    });

    if (!factionInDb) {
      throw new HttpException('No factions exist!', HttpStatus.BAD_REQUEST);
    }
    //check if password is correct
    if (factionInDb.passwordCheck(faction.factionPassword)) {
      const gameperson = await this.game_PersonRepository.create({
        faction: factionInDb,
        game: faction.game,
        role: 'soldier',
        person: person,
      });
      //check if user is already in a faction
      if (
        await this.game_PersonRepository.findOne({
          person: person,
          game: faction.game,
        })
      ) {
        throw new HttpException(
          'You have already joined faction!',
          HttpStatus.BAD_REQUEST,
        );
      }
      // insert to database
      return await this.game_PersonRepository.save(gameperson);
    } else {
      throw new HttpException('Invalid password!', HttpStatus.UNAUTHORIZED);
    }
  }

  // removes entry from gameperson
  async leaveFaction(gamepersonId) {
    await this.game_PersonRepository.delete({ gamepersonId });
    return {
      message: 'leaved faction',
    };
  }

  // changes factions multiplier to the value given to FactionDTO
  // async changeFactionMultiplier(body: FactionDTO) {
  //   const faction = await this.factionRepository.findOne({
  //     where: { factionId: body.factionId },
  //   });
  //   faction.multiplier = body.multiplier;
  //   return await this.factionRepository.save(faction);
  // }

  async promotePlayer(body) {
    const gamepersonId = body.player;
    // get playerdata
    const gameperson = await this.game_PersonRepository.findOne({
      where: { gamepersonId },
    });
    if (gameperson) {
      const promotedPlayer = await this.game_PersonRepository.create(
        gameperson,
      );
      promotedPlayer.role = body.role;
      if (body.role === 'admin') {
        promotedPlayer.faction = null;
        promotedPlayer.group = null;
      }
      return await this.game_PersonRepository.save(promotedPlayer);
    }
    throw new HttpException('player does not exist', HttpStatus.BAD_REQUEST);
  }

  // checks the password, creates an entry in GamePerson table with associated role&faction
  async createGroup(person, gameId, groupData: GameGroupDTO) {
    // get gamePerson ref
    const gamePerson = await this.game_PersonRepository.findOne({
      person: person,
      game: gameId,
    });
    // check if the authenticated person already belongs to a group
    let check = await this.game_PersonRepository.findOne({
      where: { person: person, game: gameId },
      relations: ['group'],
    });
    if (check.group) {
      throw new HttpException(
        'You already belong to a group!',
        HttpStatus.BAD_REQUEST,
      );
    }

    // create a group entry and insert it to db
    const group = await this.game_GroupRepository.create({
      ...groupData,
      leader: gamePerson,
    });
    const gameGroup = await this.game_GroupRepository.insert(group);

    // update the gamePerson entry with group data
    gamePerson.group = gamePerson.leaderGroup = gameGroup.identifiers[0]['id'];
    await this.game_PersonRepository.save(gamePerson);

    return {
      message: 'created new group',
    };
  }

  // get the groups in the given Faction
  async showGroups(factionId) {
    let players = await this.game_PersonRepository.find({
      where: { faction: factionId },
      relations: ['person', 'group'],
    });

    players.sort(function(a, b) {
      return a.person.name.localeCompare(b.person.name);
    });

    let groups = await this.game_GroupRepository.find({
      where: { faction: factionId },
      relations: ['leader', 'leader.person'],
    });

    let resObj = await Promise.all(
      groups.map(async group => {
        return await {
          id: group.id,
          name: group.name,
          class: group.class,
          leader: group.leader.person.name,
          players: [],
        };
      }),
    );

    resObj.push({
      id: 'empty-group-id',
      name: 'No group',
      class: 'infantry',
      leader: '',
      players: [],
    });

    await Promise.all(
      players.map(async player => {
        for (let i = 0; i < resObj.length; i++) {
          if (player.group == null) {
            resObj[resObj.length - 1].players.push({
              gamepersonId: player.gamepersonId,
              role: player.role,
              name: player.person.name,
            });
            break;
          }
          if (resObj[i].name == player.group.name) {
            resObj[i].players.push({
              gamepersonId: player.gamepersonId,
              role: player.role,
              name: player.person.name,
            });
            break;
          }
        }
      }),
    );

    return resObj;
  }

  // puts a non admin or faction leader player into a specified group
  async joinGroup(gameperson, data: JoinGameGroupDTO) {
    gameperson.group = data.groupId;
    await this.game_PersonRepository.save(gameperson);
    return {
      message: 'Joined group',
    };
  }

  // checks if player is in a faction and what role the player is in
  async verifyUser(person, game) {
    const gameperson = await this.game_PersonRepository.findOne({
      where: { person, game },
      relations: ['faction', 'group'],
    });
    if (gameperson && gameperson.faction) {
      return {
        factionId: gameperson.faction.factionId,
        factionName: gameperson.faction.factionName,
        role: gameperson.role,
        group: gameperson.group ? true : false,
      };
    } else {
      return gameperson ? { role: gameperson.role } : { role: '' };
    }
  }
}