import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { LoggingService } from '../database/logging/logging.service';
import { TeamSetting } from '../team-settings/entities/team-setting.entity';
import { TEAM_SETTING_DEFAULTS } from '../team-settings/team-setting-defaults';
import {
  TeamPermissionsDto,
  UpdateTeamPermissionsDto,
} from './dto/team-permissions.dto';
import { Team } from './entities/team.entity';
import { TeamSettingType } from './model/team-settings-type';
import { TeamAccessService } from './team-access.service';

type PermissionField = keyof TeamPermissionsDto;

const FIELD_TO_SETTING_KEY: Record<
  PermissionField,
  Exclude<TeamSettingType, ''>
> = {
  transactionCreateMinRole: 'transaction_create_min_role',
  transactionReverseMinRole: 'transaction_reverse_min_role',
  inviteMinRole: 'invite_min_role',
  memberManageMinRole: 'member_manage_min_role',
  penaltyManageMinRole: 'penalty_manage_min_role',
  publicAccessManageMinRole: 'public_access_manage_min_role',
};

@Injectable()
export class TeamPermissionsService {
  constructor(
    @InjectRepository(Team)
    private readonly teamRepository: Repository<Team>,
    @InjectRepository(TeamSetting)
    private readonly teamSettingRepository: Repository<TeamSetting>,
    private readonly access: TeamAccessService,
    private readonly logger: LoggingService,
  ) {}

  async getPermissions(
    userId: number,
    teamId: number,
  ): Promise<TeamPermissionsDto> {
    await this.access.assertMember(userId, teamId);
    const team = await this.loadTeam(teamId);
    return this.toDto(team.settings ?? []);
  }

  async updatePermissions(
    userId: number,
    teamId: number,
    dto: UpdateTeamPermissionsDto,
  ): Promise<TeamPermissionsDto> {
    await this.access.assertManager(userId, teamId);
    const team = await this.loadTeam(teamId);

    for (const field of Object.keys(FIELD_TO_SETTING_KEY) as PermissionField[]) {
      const value = dto[field];
      if (value === undefined) continue;
      const key = FIELD_TO_SETTING_KEY[field];
      const existing = (team.settings ?? []).find((s) => s.key === key);
      if (existing) {
        existing.value = String(value);
        await this.teamSettingRepository.save(existing);
      } else {
        await this.teamSettingRepository.save(
          this.teamSettingRepository.create({ team, key, value: String(value) }),
        );
      }
    }

    await this.logger.info({
      event: 'team_permissions_update',
      details: `teamId=${teamId} changed=${Object.keys(dto).join(',')}`,
      userId,
    });

    const updatedTeam = await this.loadTeam(teamId);
    return this.toDto(updatedTeam.settings ?? []);
  }

  private async loadTeam(teamId: number): Promise<Team> {
    const team = await this.teamRepository.findOne({
      where: { id: teamId },
      relations: ['settings'],
    });
    if (!team) throw new NotFoundException('Team nicht gefunden.');
    return team;
  }

  private toDto(settings: TeamSetting[]): TeamPermissionsDto {
    const result = {} as TeamPermissionsDto;
    for (const field of Object.keys(FIELD_TO_SETTING_KEY) as PermissionField[]) {
      const key = FIELD_TO_SETTING_KEY[field];
      const setting = settings.find((s) => s.key === key);
      const parsed = setting ? Number(setting.value) : NaN;
      result[field] =
        Number.isInteger(parsed) && parsed >= 1 && parsed <= 5
          ? parsed
          : TEAM_SETTING_DEFAULTS[key];
    }
    return result;
  }
}
