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 type { OrganizationQueryDto } from './dto/organization-query.dto.js';

/**
 * 组织域通用能力服务：
 * 负责角色校验、作用域校验、分页标准化和基础存在性检查。
 */
@Injectable()
export class OrganizationAccessService {
  constructor(private readonly prisma: PrismaService) {}

  /**
   * 校验角色是否在允许范围内。
   */
  assertRole(actor: ActorContext, roles: Role[]) {
    if (!roles.includes(actor.role)) {
      throw new ForbiddenException(MESSAGES.DEFAULT_FORBIDDEN);
    }
  }

  /**
   * 校验系统管理员权限。
   */
  assertSystemAdmin(actor: ActorContext, message: string) {
    if (actor.role !== Role.SYSTEM_ADMIN) {
      throw new ForbiddenException(message);
    }
  }

  /**
   * 校验组织域医院级作用域限制。
   */
  assertHospitalScope(actor: ActorContext, targetHospitalId: number) {
    if (actor.role === Role.SYSTEM_ADMIN) {
      return;
    }

    const hospitalScopedRoles: Role[] = [
      Role.HOSPITAL_ADMIN,
      Role.DIRECTOR,
      Role.LEADER,
    ];
    if (!hospitalScopedRoles.includes(actor.role)) {
      return;
    }

    const actorHospitalId = this.requireActorHospitalId(actor);
    if (actorHospitalId !== targetHospitalId) {
      throw new ForbiddenException(MESSAGES.ORG.HOSPITAL_ADMIN_SCOPE_INVALID);
    }
  }

  /**
   * 读取并校验当前登录上下文的医院 ID。
   */
  requireActorHospitalId(actor: ActorContext): number {
    if (
      typeof actor.hospitalId !== 'number' ||
      !Number.isInteger(actor.hospitalId) ||
      actor.hospitalId <= 0
    ) {
      throw new BadRequestException(MESSAGES.ORG.ACTOR_HOSPITAL_REQUIRED);
    }
    return actor.hospitalId;
  }

  /**
   * 读取并校验当前登录上下文的科室 ID。
   */
  requireActorDepartmentId(actor: ActorContext): number {
    if (
      typeof actor.departmentId !== 'number' ||
      !Number.isInteger(actor.departmentId) ||
      actor.departmentId <= 0
    ) {
      throw new BadRequestException(MESSAGES.ORG.ACTOR_DEPARTMENT_REQUIRED);
    }
    return actor.departmentId;
  }

  /**
   * 读取并校验当前登录上下文的小组 ID。
   */
  requireActorGroupId(actor: ActorContext): number {
    if (
      typeof actor.groupId !== 'number' ||
      !Number.isInteger(actor.groupId) ||
      actor.groupId <= 0
    ) {
      throw new BadRequestException(MESSAGES.ORG.ACTOR_GROUP_REQUIRED);
    }
    return actor.groupId;
  }

  /**
   * 分页参数标准化。
   */
  resolvePaging(query: OrganizationQueryDto) {
    const page = query.page && query.page > 0 ? query.page : 1;
    const pageSize =
      query.pageSize && query.pageSize > 0 && query.pageSize <= 100
        ? query.pageSize
        : 20;
    return {
      page,
      pageSize,
      skip: (page - 1) * pageSize,
      take: pageSize,
    };
  }

  /**
   * 名称字段标准化并确保非空。
   */
  normalizeName(value: string, message: string) {
    const trimmed = value?.trim();
    if (!trimmed) {
      throw new BadRequestException(message);
    }
    return trimmed;
  }

  /**
   * 数字参数标准化。
   */
  toInt(value: unknown, message: string) {
    const parsed = Number(value);
    if (!Number.isInteger(parsed)) {
      throw new BadRequestException(message);
    }
    return parsed;
  }

  /**
   * 确认医院存在。
   */
  async ensureHospitalExists(id: number) {
    const hospital = await this.prisma.hospital.findUnique({
      where: { id },
      select: { id: true },
    });
    if (!hospital) {
      throw new NotFoundException(MESSAGES.ORG.HOSPITAL_NOT_FOUND);
    }
    return hospital;
  }

  /**
   * 确认科室存在，并返回归属医院信息。
   */
  async ensureDepartmentExists(id: number) {
    const department = await this.prisma.department.findUnique({
      where: { id },
      select: { id: true, hospitalId: true },
    });
    if (!department) {
      throw new NotFoundException(MESSAGES.ORG.DEPARTMENT_NOT_FOUND);
    }
    return department;
  }

  /**
   * 统一处理删除冲突（存在外键引用）。
   */
  handleDeleteConflict(error: unknown) {
    if (
      error instanceof Prisma.PrismaClientKnownRequestError &&
      (error.code === 'P2003' || error.code === 'P2014')
    ) {
      throw new ConflictException(MESSAGES.ORG.DELETE_CONFLICT);
    }
  }
}
