import {
  Injectable,
  NotFoundException,
  BadRequestException,
  ConflictException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { TeamMember } from '../../entities/team-member.entity';
import { Role } from '../../entities/role.entity';
import { User } from '../../entities/user.entity';
import { InviteMemberDto } from './dto/invite-member.dto';
import { CreateRoleDto } from './dto/create-role.dto';
import { UpdateRoleDto } from './dto/update-role.dto';

@Injectable()
export class TeamService {
  constructor(
    @InjectRepository(TeamMember)
    private teamMemberRepository: Repository<TeamMember>,
    @InjectRepository(Role)
    private roleRepository: Repository<Role>,
    @InjectRepository(User)
    private userRepository: Repository<User>,
  ) {}

  async getTeam(licenseId: string) {
    // Get all team members with joined user and role data
    const members = await this.teamMemberRepository.find({
      where: { license_id: licenseId },
      relations: ['user', 'role'],
      order: { accepted_at: 'DESC' },
    });

    // Get all roles (system defaults + custom roles for this license)
    const roles = await this.getRoles(licenseId);

    return {
      members: members.map((member) => ({
        id: member.id,
        user_id: member.user_id,
        username: member.user?.username,
        email: member.user?.email,
        role_id: member.role_id,
        role_name: member.role?.role_name,
        accepted_at: member.accepted_at,
        invited_by: member.invited_by,
      })),
      roles,
    };
  }

  async inviteMember(
    licenseId: string,
    invitedBy: string,
    dto: InviteMemberDto,
  ) {
    // Look up user by email
    const user = await this.userRepository.findOne({
      where: { email: dto.email },
    });

    if (!user) {
      throw new NotFoundException(
        `User with email ${dto.email} not found. User must register first.`,
      );
    }

    // Check if user is already a team member
    const existingMember = await this.teamMemberRepository.findOne({
      where: {
        license_id: licenseId,
        user_id: user.id,
      },
    });

    if (existingMember) {
      throw new ConflictException(
        `User ${dto.email} is already a team member`,
      );
    }

    // Verify role exists and belongs to this license or is a system default
    const role = await this.roleRepository.findOne({
      where: { id: dto.role_id },
    });

    if (!role) {
      throw new NotFoundException(`Role ${dto.role_id} not found`);
    }

    if (role.license_id !== licenseId && !role.is_system_default) {
      throw new BadRequestException(
        'Cannot assign role from another license',
      );
    }

    // Create team member entry
    const teamMember = this.teamMemberRepository.create({
      license_id: licenseId,
      user_id: user.id,
      role_id: dto.role_id,
      invited_by: invitedBy,
      accepted_at: new Date(),
    });

    const saved = await this.teamMemberRepository.save(teamMember);

    // Return with joined data
    const memberWithData = await this.teamMemberRepository.findOne({
      where: { id: saved.id },
      relations: ['user', 'role'],
    });

    if (!memberWithData) {
      throw new NotFoundException(`Team member ${saved.id} not found after creation`);
    }

    return {
      id: memberWithData.id,
      user_id: memberWithData.user_id,
      username: memberWithData.user?.username,
      email: memberWithData.user?.email,
      role_id: memberWithData.role_id,
      role_name: memberWithData.role?.role_name,
      accepted_at: memberWithData.accepted_at,
      invited_by: memberWithData.invited_by,
    };
  }

  async removeMember(licenseId: string, memberId: string) {
    const member = await this.teamMemberRepository.findOne({
      where: {
        id: memberId,
        license_id: licenseId,
      },
    });

    if (!member) {
      throw new NotFoundException(`Team member ${memberId} not found`);
    }

    await this.teamMemberRepository.delete(memberId);

    return { deleted: true };
  }

  async getRoles(licenseId: string) {
    // Get all roles where license_id matches OR is_system_default = true
    const roles = await this.roleRepository.find({
      where: [
        { license_id: licenseId },
        { is_system_default: true },
      ],
      order: { is_system_default: 'DESC', role_name: 'ASC' },
    });

    return roles;
  }

  async createRole(licenseId: string, dto: CreateRoleDto) {
    // Verify role name doesn't already exist for this license
    const existing = await this.roleRepository.findOne({
      where: {
        license_id: licenseId,
        role_name: dto.role_name,
      },
    });

    if (existing) {
      throw new ConflictException(
        `Role with name "${dto.role_name}" already exists`,
      );
    }

    const role = this.roleRepository.create({
      license_id: licenseId,
      role_name: dto.role_name,
      permissions: dto.permissions,
      is_system_default: false,
    });

    return await this.roleRepository.save(role);
  }

  async updateRole(licenseId: string, roleId: string, dto: UpdateRoleDto) {
    const role = await this.roleRepository.findOne({
      where: { id: roleId },
    });

    if (!role) {
      throw new NotFoundException(`Role ${roleId} not found`);
    }

    // Cannot update system default roles
    if (role.is_system_default) {
      throw new BadRequestException('Cannot modify system default roles');
    }

    // Cannot update roles from other licenses
    if (role.license_id !== licenseId) {
      throw new BadRequestException('Cannot modify role from another license');
    }

    // Check for name conflicts if updating name
    if (dto.role_name && dto.role_name !== role.role_name) {
      const existing = await this.roleRepository.findOne({
        where: {
          license_id: licenseId,
          role_name: dto.role_name,
        },
      });

      if (existing) {
        throw new ConflictException(
          `Role with name "${dto.role_name}" already exists`,
        );
      }
    }

    Object.assign(role, dto);
    return await this.roleRepository.save(role);
  }

  async deleteRole(licenseId: string, roleId: string) {
    const role = await this.roleRepository.findOne({
      where: { id: roleId },
    });

    if (!role) {
      throw new NotFoundException(`Role ${roleId} not found`);
    }

    // Cannot delete system default roles
    if (role.is_system_default) {
      throw new BadRequestException('Cannot delete system default roles');
    }

    // Cannot delete roles from other licenses
    if (role.license_id !== licenseId) {
      throw new BadRequestException('Cannot delete role from another license');
    }

    // Check if role is in use
    const membersUsingRole = await this.teamMemberRepository.count({
      where: { role_id: roleId },
    });

    if (membersUsingRole > 0) {
      throw new BadRequestException(
        `Cannot delete role: ${membersUsingRole} team member(s) currently assigned to this role`,
      );
    }

    await this.roleRepository.delete(roleId);

    return { deleted: true };
  }
}
