import { ForbiddenException, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Player } from '../players/entities/player.entity';
import { RoleEnum } from '../roles/roles.enum';
import { TeamRolesEnum } from '../team-roles/team-roles.enum';
import { TeamSetting } from '../team-settings/entities/team-setting.entity';
import { TeamSettingType } from './model/team-settings-type';
import { User } from '../users/entities/user.entity';
import { EntityManager, Repository } from 'typeorm';

@Injectable()
export class TeamAccessService {
  constructor(
    @InjectRepository(User)
    private readonly userRepository: Repository<User>,
    @InjectRepository(Player)
    private readonly playerRepository: Repository<Player>,
    @InjectRepository(TeamSetting)
    private readonly teamSettingRepository: Repository<TeamSetting>,
  ) {}

  async assertMember(
    userId: number,
    teamId: number,
    manager?: EntityManager,
  ): Promise<void> {
    await this.assertMinimumRole(userId, teamId, 1, manager);
  }

  async assertManager(
    userId: number,
    teamId: number,
    manager?: EntityManager,
  ): Promise<void> {
    await this.assertMinimumRole(userId, teamId, 3, manager);
  }

  async assertAtLeast(
    userId: number,
    teamId: number,
    settingKey: Exclude<TeamSettingType, ''>,
    fallbackMinRole: TeamRolesEnum,
    manager?: EntityManager,
  ): Promise<void> {
    await this.assertMinimumRole(
      userId,
      teamId,
      () => this.resolveMinimumRole(teamId, settingKey, fallbackMinRole, manager),
      manager,
    );
  }

  private async resolveMinimumRole(
    teamId: number,
    settingKey: Exclude<TeamSettingType, ''>,
    fallbackMinRole: TeamRolesEnum,
    manager?: EntityManager,
  ): Promise<number> {
    const teamSettingRepository =
      manager?.getRepository(TeamSetting) ?? this.teamSettingRepository;
    const setting = await teamSettingRepository.findOne({
      where: { team: { id: teamId }, key: settingKey },
    });
    const parsed = setting ? Number(setting.value) : NaN;
    return Number.isInteger(parsed) && parsed >= 1 && parsed <= 5
      ? parsed
      : fallbackMinRole;
  }

  private async assertMinimumRole(
    userId: number,
    teamId: number,
    minimumRole: number | (() => Promise<number>),
    manager?: EntityManager,
  ): Promise<void> {
    const userRepository = manager?.getRepository(User) ?? this.userRepository;
    const playerRepository =
      manager?.getRepository(Player) ?? this.playerRepository;
    const user = await userRepository.findOne({ where: { id: userId } });
    if (user?.role?.id === RoleEnum.admin) return;

    const resolvedMinimumRole =
      typeof minimumRole === 'function' ? await minimumRole() : minimumRole;

    const players = await playerRepository.find({
      where: { user: { id: userId }, team: { id: teamId } },
    });
    const highestActiveRole = players
      .filter((player) => player.active)
      .reduce(
        (highest, player) => Math.max(highest, player.teamRole?.id ?? 0),
        0,
      );

    if (highestActiveRole < resolvedMinimumRole) {
      throw new ForbiddenException('Keine Berechtigung für dieses Team.');
    }
  }
}
