import {
  Injectable,
  Logger,
  NotFoundException,
  ConflictException,
} from '@nestjs/common';
import { PrismaService } from '@core/database/prisma/prisma.service';
import { CreateRoleDto, UpdateRoleDto, RoleQueryDto } from './dto/role.dto';
import {
  IamSystemRoleProtectedException,
  IamRoleHasUsersException,
} from '../exceptions';
import { AIToolsService } from '../ai-tools/ai-tools.service';
import { EMPLOYEE_BASELINE_TOOLS } from '../ai-tools/available-tools.config';

@Injectable()
export class RolesService {
  private readonly logger = new Logger(RolesService.name);

  constructor(
    private prisma: PrismaService,
    private aiToolsService: AIToolsService,
  ) {}

  /**
   * Get all roles
   */
  async findAll(query?: RoleQueryDto) {
    const where = query?.keyword
      ? {
          OR: [
            { name: { contains: query.keyword, mode: 'insensitive' as const } },
            { code: { contains: query.keyword, mode: 'insensitive' as const } },
            { description: { contains: query.keyword, mode: 'insensitive' as const } },
          ],
        }
      : {};

    const roles = await this.prisma.role.findMany({
      where,
      include: {
        _count: {
          select: {
            users: true,
            permissions: true,
          },
        },
      },
      orderBy: { createdAt: 'desc' },
    });

    return roles.map((role) => {
      const { _count, ...rest } = role;
      return {
        ...rest,
        userCount: _count.users,
        permissionCount: _count.permissions,
      };
    });
  }

  /**
   * Get role by ID
   */
  async findOne(id: string) {
    const role = await this.prisma.role.findUnique({
      where: { id },
      include: {
        permissions: {
          include: {
            permission: true,
          },
        },
        users: {
          include: {
            user: {
              select: {
                id: true,
                username: true,
                displayName: true,
                email: true,
                avatar: true,
                departmentMemberships: {
                  where: { isPrimary: true },
                  include: {
                    department: {
                      select: { id: true, name: true },
                    },
                  },
                },
              },
            },
          },
        },
      },
    });

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

    return {
      ...role,
      permissions: (role as any).permissions?.map((rp: any) => rp.permission) || [],
      users: (role as any).users?.map((ur: any) => ur.user) || [],
    };
  }

  /**
   * Create role
   */
  async create(createRoleDto: CreateRoleDto) {
    // Check if code or name already exists
    const existingRole = await this.prisma.role.findFirst({
      where: {
        OR: [{ code: createRoleDto.code }, { name: createRoleDto.name }],
      },
    });

    if (existingRole) {
      if (existingRole.code === createRoleDto.code) {
        throw new ConflictException({
          statusCode: 409,
          message: `角色代码已存在`,
          error: 'ROLE_CODE_EXISTS',
        });
      } else {
        throw new ConflictException({
          statusCode: 409,
          message: `角色名称已存在`,
          error: 'ROLE_NAME_EXISTS',
        });
      }
    }

    const { permissionIds, ...roleData } = createRoleDto;

    const role = await this.prisma.role.create({
      data: {
        ...roleData,
        isBuiltIn: false,
        enabled: true,
        permissions: permissionIds
          ? {
              create: permissionIds.map((permissionId) => ({
                permission: { connect: { id: permissionId } },
              })),
            }
          : undefined,
      },
      include: {
        permissions: {
          include: {
            permission: true,
          },
        },
      },
    });

    // 给新角色种入 EMPLOYEE_BASELINE_TOOLS（v2.3 全量 24 项）。
    // 失败不回滚 role：role 已创建是主操作；list API 端 LEFT JOIN 会兜底显示空授权角色，
    // 管理员能看见并手动授权。
    if (role.code !== 'SyncBot') {
      try {
        await this.aiToolsService.setRoleGrants(role.id, [...EMPLOYEE_BASELINE_TOOLS]);
      } catch (err) {
        this.logger.error(
          `Failed to seed AI tool grants for role ${role.code} (${role.id})`,
          err instanceof Error ? err.stack : String(err),
        );
      }
    }

    return {
      ...role,
      permissions: role.permissions.map((rp) => rp.permission),
    };
  }

  /**
   * Update role
   */
  async update(id: string, updateRoleDto: UpdateRoleDto) {
    const role = await this.prisma.role.findUnique({ where: { id } });

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

    // Built-in roles can have name/description/enabled changed, but not deleted
    // Check for name conflicts
    if (updateRoleDto.name) {
      const existingRole = await this.prisma.role.findFirst({
        where: {
          name: updateRoleDto.name,
          id: { not: id },
        },
      });

      if (existingRole) {
        throw new ConflictException(`Role name '${updateRoleDto.name}' already exists`);
      }
    }

    return this.prisma.role.update({
      where: { id },
      data: updateRoleDto,
      include: {
        _count: {
          select: {
            users: true,
            permissions: true,
          },
        },
      },
    });
  }

  /**
   * Delete role
   */
  async remove(id: string) {
    const role = await this.prisma.role.findUnique({
      where: { id },
      include: {
        _count: {
          select: { users: true },
        },
      },
    });

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

    if (role.isBuiltIn) {
      throw new IamSystemRoleProtectedException(role.name);
    }

    if (role._count.users > 0) {
      throw new IamRoleHasUsersException(role.name, role._count.users);
    }

    await this.prisma.role.delete({ where: { id } });

    return { message: 'Role deleted successfully' };
  }

  /**
   * Get role permissions
   */
  async getPermissions(id: string) {
    const role = await this.prisma.role.findUnique({
      where: { id },
      include: {
        permissions: {
          include: {
            permission: true,
          },
        },
      },
    });

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

    return (role as any).permissions?.map((rp: any) => rp.permission) || [];
  }

  /**
   * Assign permissions to role (replace existing)
   */
  async assignPermissions(id: string, permissionIds: string[]) {
    const role = await this.prisma.role.findUnique({ where: { id } });

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

    // Validate all permission IDs
    const permissions = await this.prisma.permission.findMany({
      where: { id: { in: permissionIds } },
    });

    if (permissions.length !== permissionIds.length) {
      throw new NotFoundException('Some permission IDs are invalid');
    }

    // Delete existing and create new associations
    await this.prisma.$transaction([
      this.prisma.rolePermission.deleteMany({ where: { roleId: id } }),
      this.prisma.rolePermission.createMany({
        data: permissionIds.map((permissionId) => ({
          roleId: id,
          permissionId,
        })),
      }),
    ]);

    // TODO: Clear permission cache for all users with this role

    const assignedPermissions = await this.getPermissions(id);

    return {
      roleId: id,
      permissionCount: assignedPermissions.length,
    };
  }

  /**
   * Get role users
   */
  async getUsers(id: string) {
    const role = await this.prisma.role.findUnique({
      where: { id },
      include: {
        users: {
          include: {
            user: {
              select: {
                id: true,
                username: true,
                displayName: true,
                email: true,
                avatar: true,
                status: true,
                departmentMemberships: {
                  where: { isPrimary: true },
                  include: {
                    department: {
                      select: { id: true, name: true },
                    },
                    position: {
                      select: { id: true, name: true },
                    },
                  },
                },
              },
            },
          },
        },
      },
    });

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

    return (role as any).users?.map((ur: any) => ur.user) || [];
  }

  // NOTE: 用户角色分配应通过 UsersService.assignRoles() 完成
  // 角色移除应通过 UsersService.removeRole() 完成
  // 参见: POST /users/:id/roles 和 DELETE /users/:id/roles/:roleId
}
