import {
  BadRequestException,
  ConflictException,
  ForbiddenException,
  Injectable,
  NotFoundException,
} from '@nestjs/common';
import { Prisma } from '../generated/prisma/client.js';
import { Role } from '../generated/prisma/enums.js';
import type { ActorContext } from '../common/actor-context.js';
import { MESSAGES } from '../common/messages.js';
import { PrismaService } from '../prisma.service.js';
import { OrganizationAccessService } from '../organization-common/organization-access.service.js';
import { CreateGroupDto } from './dto/create-group.dto.js';
import { UpdateGroupDto } from './dto/update-group.dto.js';
import { OrganizationQueryDto } from '../organization-common/dto/organization-query.dto.js';

/**
 * 小组资源服务：聚焦小组实体 CRUD 与院级作用域约束。
 */
@Injectable()
export class GroupsService {
  constructor(
    private readonly prisma: PrismaService,
    private readonly access: OrganizationAccessService,
  ) {}

  /**
   * 创建小组：系统管理员可跨院；院管仅可在本院。
   */
  async create(actor: ActorContext, dto: CreateGroupDto) {
    this.access.assertRole(actor, [Role.SYSTEM_ADMIN, Role.HOSPITAL_ADMIN]);
    const departmentId = this.access.toInt(
      dto.departmentId,
      MESSAGES.ORG.DEPARTMENT_ID_REQUIRED,
    );
    const department = await this.access.ensureDepartmentExists(departmentId);
    if (actor.role === Role.HOSPITAL_ADMIN) {
      this.access.assertHospitalScope(actor, department.hospitalId);
    }

    return this.prisma.group.create({
      data: {
        name: this.access.normalizeName(
          dto.name,
          MESSAGES.ORG.GROUP_NAME_REQUIRED,
        ),
        departmentId,
      },
    });
  }

  /**
   * 查询小组列表：院管限定本院。
   */
  async findAll(actor: ActorContext, query: OrganizationQueryDto) {
    this.access.assertRole(actor, [
      Role.SYSTEM_ADMIN,
      Role.HOSPITAL_ADMIN,
      Role.DIRECTOR,
      Role.LEADER,
      Role.DOCTOR,
    ]);
    const paging = this.access.resolvePaging(query);
    const where: Prisma.GroupWhereInput = {};

    if (query.keyword) {
      where.name = { contains: query.keyword.trim(), mode: 'insensitive' };
    }
    if (query.departmentId != null) {
      where.departmentId = this.access.toInt(
        query.departmentId,
        MESSAGES.ORG.DEPARTMENT_ID_REQUIRED,
      );
    }

    if (actor.role === Role.HOSPITAL_ADMIN) {
      where.department = {
        hospitalId: this.access.requireActorHospitalId(actor),
      };
    } else if (
      actor.role === Role.DIRECTOR ||
      actor.role === Role.LEADER ||
      actor.role === Role.DOCTOR
    ) {
      where.departmentId = this.access.requireActorDepartmentId(actor);
    } else if (query.hospitalId != null) {
      where.department = {
        hospitalId: this.access.toInt(
          query.hospitalId,
          MESSAGES.ORG.HOSPITAL_ID_REQUIRED,
        ),
      };
    }

    const [total, list] = await this.prisma.$transaction([
      this.prisma.group.count({ where }),
      this.prisma.group.findMany({
        where,
        include: {
          department: { include: { hospital: true } },
          _count: { select: { users: true } },
        },
        skip: paging.skip,
        take: paging.take,
        orderBy: { id: 'desc' },
      }),
    ]);

    return { total, ...paging, list };
  }

  /**
   * 查询小组详情：院管仅可查看本院。
   */
  async findOne(actor: ActorContext, id: number) {
    this.access.assertRole(actor, [
      Role.SYSTEM_ADMIN,
      Role.HOSPITAL_ADMIN,
      Role.DIRECTOR,
      Role.LEADER,
      Role.DOCTOR,
    ]);
    const groupId = this.access.toInt(id, MESSAGES.ORG.GROUP_ID_REQUIRED);
    const group = await this.prisma.group.findUnique({
      where: { id: groupId },
      include: {
        department: { include: { hospital: true } },
        _count: { select: { users: true } },
      },
    });
    if (!group) {
      throw new NotFoundException(MESSAGES.ORG.GROUP_NOT_FOUND);
    }
    if (actor.role === Role.HOSPITAL_ADMIN) {
      this.access.assertHospitalScope(actor, group.department.hospital.id);
    } else if (
      actor.role === Role.DIRECTOR ||
      actor.role === Role.LEADER ||
      actor.role === Role.DOCTOR
    ) {
      const actorDepartmentId = this.access.requireActorDepartmentId(actor);
      if (group.department.id !== actorDepartmentId) {
        throw new ForbiddenException(MESSAGES.ORG.HOSPITAL_ADMIN_SCOPE_INVALID);
      }
    }
    return group;
  }

  /**
   * 更新小组：院管仅可修改本院。
   */
  async update(actor: ActorContext, id: number, dto: UpdateGroupDto) {
    const current = await this.findOne(actor, id);
    const data: Prisma.GroupUpdateInput = {};

    if (dto.departmentId !== undefined) {
      throw new BadRequestException(MESSAGES.ORG.GROUP_REPARENT_FORBIDDEN);
    }

    if (dto.name !== undefined) {
      data.name = this.access.normalizeName(
        dto.name,
        MESSAGES.ORG.GROUP_NAME_REQUIRED,
      );
    }

    return this.prisma.group.update({
      where: { id: current.id },
      data,
    });
  }

  /**
   * 删除小组：院管仅可删除本院。
   */
  async remove(actor: ActorContext, id: number) {
    this.access.assertRole(actor, [Role.SYSTEM_ADMIN, Role.HOSPITAL_ADMIN]);
    const current = await this.findOne(actor, id);

    // 业务层先拦截，给前端稳定中文提示；数据库层仍保留 RESTRICT 兜底。
    if (current._count.users > 0) {
      throw new ConflictException(MESSAGES.ORG.GROUP_DELETE_HAS_USERS);
    }

    try {
      return await this.prisma.group.delete({ where: { id: current.id } });
    } catch (error) {
      this.access.handleDeleteConflict(error);
      throw error;
    }
  }
}
