import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { LoggingService } from 'src/database/logging/logging.service';
import { Player } from 'src/players/entities/player.entity';
import { TeamRole } from 'src/team-roles/entities/team-roles.entity';
import { CreateTeamSettingDTO } from 'src/team-settings/dto/create-team-setting.dto';
import { TeamSetting } from 'src/team-settings/entities/team-setting.entity';
import { TeamWalletTransaction } from 'src/team-wallet-transactions/entities/team-wallet-transaction.entity';
import { Transaction } from 'src/transactions/entitites/transaction.entity';
import { Repository } from 'typeorm';
import { CreateTeamDTO } from './dto/create-team.dto';
import { Team } from './entities/team.entity';

@Injectable()
export class TeamsService {
  constructor(
    @InjectRepository(Team)
    private repository: Repository<Team>,
    @InjectRepository(Player)
    private playerRepository: Repository<Player>,
    @InjectRepository(Transaction)
    private transactionsRepository: Repository<Transaction>,
    @InjectRepository(TeamRole)
    private rolesRepository: Repository<TeamRole>,
    @InjectRepository(TeamSetting)
    private settingsRepository: Repository<TeamSetting>,
    @InjectRepository(TeamWalletTransaction)
    private teamWalletTransactionRepository: Repository<TeamWalletTransaction>,
    private logger: LoggingService,
  ) {}

  async getOverview(teamId: string) {
    const id = Number(teamId);

    const team = await this.repository.findOneOrFail({
      where: { id },
      relations: ['players'],
    });

    if (team.players) {
      const out = team.players
        .filter((p) => p.active)
        .reduce((acc, succ) => acc + Number(succ.balance), 0);
      team.outstanding = out * -1;
    } else {
      team.outstanding = 0;
    }
    team.balance = Number(team.balance);

    return team;
  }

  async getOverviewByAlias(alias: string) {
    const team = await this.repository.findOneOrFail({
      where: { alias },
      relations: ['players'],
    });

    if (team.players) {
      const out = team.players
        .filter((p) => p.active)
        .reduce((acc, succ) => acc + Number(succ.balance), 0);
      team.outstanding = out * -1;
    } else {
      team.outstanding = 0;
    }
    return team;
  }

  async getUserTransactionsFromTeam(teamId: string, userId: string) {
    const player = await this.playerRepository.findOne({
      where: { id: Number(userId) },
      relations: ['transactions'],
    });

    if (!player) {
      throw new HttpException(
        {
          status: HttpStatus.NOT_FOUND,
          errors: {
            user: 'user_not_found',
          },
        },
        HttpStatus.NOT_FOUND,
      );
    }
    const transactions = await this.transactionsRepository.find({
      where: {
        player: {
          id: player.id,
        },
      },
      order: {
        date: 'DESC',
      },
      take: 20,
    });
    return transactions;
  }

  async createNewPlayer(
    id: string,
    player: { firstName: string; lastName: string; teamRole: any },
  ) {
    if (typeof player.teamRole == 'string') {
      player.teamRole = await this.rolesRepository.findOneBy({
        name: player.teamRole,
      });
    } else if (typeof player.teamRole == 'number') {
      player.teamRole = await this.rolesRepository.findOneBy({
        id: player.teamRole,
      });
    } else if (!player.teamRole) {
      player.teamRole = await this.rolesRepository.findOneBy({
        id: 1, // player
      });
    }
    const team = await this.repository.findOneBy({
      id: Number(id),
    });

    if (!team) {
      throw new HttpException('No Team Found', HttpStatus.UNPROCESSABLE_ENTITY);
    }
    const p = {
      ...player,
      team,
    };

    const created: Player = this.playerRepository.create(p);

    const playerSaved: Player = await this.playerRepository.save(created);

    await this.logger.info({
      event: 'player_creation',
      details: `Spieler ${playerSaved.id}, ${p.firstName} ${p.lastName} erstellt`,
      userId: Number(id),
    });
    return playerSaved;
  }

  async createNewTeam(teamdto: CreateTeamDTO, userId: string) {
    const createTeam = this.repository.create(teamdto);

    const team = await this.repository.save(createTeam);
    await this.generateBasicTeamSettings(team);

    await this.logger.info({
      event: 'team_create',
      details: `created team id: ${team.id}`,
      userId: Number(userId),
    });
    return team;
  }

  private generateBasicTeamSettings(team: Team): Promise<void> {
    return new Promise<void>(async (resolve) => {
      const s: CreateTeamSettingDTO = {
        key: 'transaction_create_min_role',
        value: '2',
        team,
      };

      const setting = this.settingsRepository.create(s);
      await this.settingsRepository.save(setting);
      resolve();
    });
  }

  async getTeamTransactions(teamId: string | number, userId: string | number) {
    const start = new Date();
    teamId = Number(teamId);

    const team = await this.repository.findOneOrFail({
      where: {
        id: teamId,
      },
      relations: ['players', 'players.transactions', 'transactions'],
    });

    const transactions = [];

    for (const t of team.transactions) {
      transactions.push({
        id: t.id,
        date: t.date,
        amount: t.amount,
        type: t.type.name,
        note: t.note,
        isTeamWalletTransaction: true,
      });
    }

    for (const p of team.players) {
      for (const t of p.transactions) {
        transactions.push({
          id: t.id,
          date: t.date,
          amount: t.amount,
          type: t.type.name,
          note: t.note,
          playerName: p.firstName + ' ' + p.lastName,
          isTeamWalletTransaction: false,
        });
      }
    }

    const result = transactions.sort((a, b) => (a.date < b.date ? 1 : -1));
    const duration = new Date().getTime() - start.getTime();

    await this.logger.debug({
      event: 'team_transaction_get',
      details: `load ${result.length} transactions for TeamID: ${team.id}`,
      userId: Number(userId),
      duration,
    });
    return result;
  }

  async updatePlayer(playerDTO: any) {
    const player = await this.playerRepository.findOneOrFail({
      where: {
        id: playerDTO.id,
      },
      relations: ['teamRole', 'transactions', 'team', 'transactions.type'],
    });

    player.active = playerDTO.active;
    player.balance = playerDTO.balance;
    player.firstName = playerDTO.firstName;
    player.lastName = playerDTO.lastName;

    if (player.teamRole.id != playerDTO.teamRole.id) {
      player.teamRole = await this.rolesRepository.findOneByOrFail({
        id: playerDTO.reamRole.id,
      });
    }
    let balance = player.balance;
    if (!player.active && player.balance != 0) {
      player.balance = 0;
    } else if (player.active && player.balance == 0) {
      balance = player.transactions.reduce((acc, succ) => {
        let b = succ.amount;
        if (succ.type?.id > 10 && succ.amount > 0) {
          b = b * -1;
        }
        return acc + b;
      }, 0);
      player.balance = balance;
    }
    const res = await this.playerRepository.save(player);
    return res;
  }
}
