import {
  BadRequestException,
  ConflictException,
  ForbiddenException,
  Injectable,
  NotFoundException,
} from '@nestjs/common';
import { Prisma } from '../generated/prisma/client.js';
import { DeviceStatus, Role } from '../generated/prisma/enums.js';
import type { ActorContext } from '../common/actor-context.js';
import { MESSAGES } from '../common/messages.js';
import {
  normalizePressureLabelList,
  normalizePressureLabel,
} from '../common/pressure-level.util.js';
import { PrismaService } from '../prisma.service.js';
import { CreateImplantCatalogDto } from './dto/create-implant-catalog.dto.js';
import { CreateDeviceDto } from './dto/create-device.dto.js';
import { DeviceQueryDto } from './dto/device-query.dto.js';
import { UpdateImplantCatalogDto } from './dto/update-implant-catalog.dto.js';
import { UpdateDeviceDto } from './dto/update-device.dto.js';

const CATALOG_SELECT = {
  id: true,
  modelCode: true,
  manufacturer: true,
  name: true,
  isValve: true,
  pressureLevels: true,
  isPressureAdjustable: true,
  notes: true,
  createdAt: true,
  updatedAt: true,
} as const;

const DEVICE_DETAIL_INCLUDE = {
  patient: {
    select: {
      id: true,
      name: true,
      inpatientNo: true,
      phone: true,
      hospitalId: true,
      hospital: {
        select: {
          id: true,
          name: true,
        },
      },
      doctor: {
        select: {
          id: true,
          name: true,
          role: true,
        },
      },
    },
  },
  surgery: {
    select: {
      id: true,
      surgeryDate: true,
      surgeryName: true,
      surgeonName: true,
    },
  },
  implantCatalog: {
    select: CATALOG_SELECT,
  },
  _count: {
    select: {
      taskItems: true,
    },
  },
} as const;

/**
 * 设备服务：承载患者植入实例 CRUD 与全局植入物目录维护。
 */
@Injectable()
export class DevicesService {
  constructor(private readonly prisma: PrismaService) {}

  /**
   * 查询设备列表：系统管理员可跨院查询，院管仅限本院。
   */
  async findAll(actor: ActorContext, query: DeviceQueryDto) {
    this.assertAdmin(actor);

    const paging = this.resolvePaging(query);
    const scopedHospitalId = this.resolveScopedHospitalId(
      actor,
      query.hospitalId,
    );
    const where = this.buildListWhere(query, scopedHospitalId);

    const [total, list] = await this.prisma.$transaction([
      this.prisma.device.count({ where }),
      this.prisma.device.findMany({
        where,
        include: DEVICE_DETAIL_INCLUDE,
        skip: paging.skip,
        take: paging.take,
        orderBy: { id: 'desc' },
      }),
    ]);

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

  /**
   * 查询设备详情。
   */
  async findOne(actor: ActorContext, id: number) {
    this.assertAdmin(actor);

    const deviceId = this.toInt(id, 'id');
    const device = await this.prisma.device.findUnique({
      where: { id: deviceId },
      include: DEVICE_DETAIL_INCLUDE,
    });

    if (!device) {
      throw new NotFoundException(MESSAGES.DEVICE.NOT_FOUND);
    }

    this.assertDeviceReadable(actor, device.patient.hospitalId);
    return device;
  }

  /**
   * 创建设备：归属患者必须在当前管理员可写范围内。
   */
  async create(actor: ActorContext, dto: CreateDeviceDto) {
    this.assertAdmin(actor);

    const patient = await this.resolveWritablePatient(actor, dto.patientId);

    return this.prisma.device.create({
      data: {
        // 当前压力只允许由调压任务流转维护，手工创建设备时先置 0。
        currentPressure: '0',
        status: dto.status ?? DeviceStatus.ACTIVE,
        patientId: patient.id,
      },
      include: DEVICE_DETAIL_INCLUDE,
    });
  }

  /**
   * 更新设备：允许修改状态和归属患者；当前压力仅由任务完成时更新。
   */
  async update(actor: ActorContext, id: number, dto: UpdateDeviceDto) {
    const current = await this.findOne(actor, id);

    const data: Prisma.DeviceUpdateInput = {};
    if (dto.status !== undefined) {
      data.status = this.normalizeStatus(dto.status);
    }
    if (dto.patientId !== undefined) {
      const patient = await this.resolveWritablePatient(actor, dto.patientId);
      data.patient = { connect: { id: patient.id } };
    }

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

  /**
   * 删除设备：若设备已被任务明细引用，则返回 409。
   */
  async remove(actor: ActorContext, id: number) {
    const current = await this.findRemovableDevice(actor, id);
    const relatedTaskCount = await this.prisma.taskItem.count({
      where: { deviceId: current.id },
    });

    if (relatedTaskCount > 0) {
      throw new ConflictException(MESSAGES.DEVICE.DELETE_CONFLICT);
    }

    try {
      return await this.prisma.device.delete({
        where: { id: current.id },
        include: DEVICE_DETAIL_INCLUDE,
      });
    } catch (error) {
      if (
        error instanceof Prisma.PrismaClientKnownRequestError &&
        (error.code === 'P2003' || error.code === 'P2014')
      ) {
        throw new ConflictException(MESSAGES.DEVICE.DELETE_CONFLICT);
      }
      throw error;
    }
  }

  /**
   * 查询当前角色可见的植入物型号字典。
   */
  async findCatalogs(actor: ActorContext, keyword?: string) {
    this.assertCatalogReadable(actor);

    const where = this.buildCatalogWhere(keyword);

    return this.prisma.implantCatalog.findMany({
      where,
      select: CATALOG_SELECT,
      orderBy: [{ manufacturer: 'asc' }, { name: 'asc' }, { modelCode: 'asc' }],
    });
  }

  /**
   * 新增植入物型号字典。
   */
  async createCatalog(actor: ActorContext, dto: CreateImplantCatalogDto) {
    this.assertSystemAdmin(actor);
    const isValve = dto.isValve ?? true;
    const isPressureAdjustable = isValve;

    try {
      return await this.prisma.implantCatalog.create({
        data: {
          modelCode: this.normalizeModelCode(dto.modelCode),
          manufacturer: this.normalizeRequiredString(
            dto.manufacturer,
            'manufacturer',
          ),
          name: this.normalizeRequiredString(dto.name, 'name'),
          isValve,
          pressureLevels: this.normalizePressureLevels(
            dto.pressureLevels,
            isValve,
          ),
          isPressureAdjustable,
          notes:
            dto.notes === undefined
              ? undefined
              : this.normalizeNullableString(dto.notes, 'notes'),
        },
        select: CATALOG_SELECT,
      });
    } catch (error) {
      if (
        error instanceof Prisma.PrismaClientKnownRequestError &&
        error.code === 'P2002'
      ) {
        throw new ConflictException(MESSAGES.DEVICE.CATALOG_MODEL_DUPLICATE);
      }
      throw error;
    }
  }

  /**
   * 更新植入物型号字典。
   */
  async updateCatalog(
    actor: ActorContext,
    id: number,
    dto: UpdateImplantCatalogDto,
  ) {
    this.assertSystemAdmin(actor);
    const current = await this.findWritableCatalog(id);
    const nextIsValve = dto.isValve ?? current.isValve;
    const nextIsPressureAdjustable = nextIsValve;

    const data: Prisma.ImplantCatalogUpdateInput = {};
    if (dto.modelCode !== undefined) {
      data.modelCode = this.normalizeModelCode(dto.modelCode);
    }
    if (dto.manufacturer !== undefined) {
      data.manufacturer = this.normalizeRequiredString(
        dto.manufacturer,
        'manufacturer',
      );
    }
    if (dto.name !== undefined) {
      data.name = this.normalizeRequiredString(dto.name, 'name');
    }
    if (dto.isValve !== undefined) {
      data.isValve = dto.isValve;
      data.isPressureAdjustable = nextIsPressureAdjustable;
    }
    if (dto.pressureLevels !== undefined || dto.isValve !== undefined) {
      data.pressureLevels = this.normalizePressureLevels(
        dto.pressureLevels ?? current.pressureLevels,
        nextIsValve,
      );
    }
    if (dto.notes !== undefined) {
      data.notes = this.normalizeNullableString(dto.notes, 'notes');
    }

    try {
      return await this.prisma.implantCatalog.update({
        where: { id: current.id },
        data,
        select: CATALOG_SELECT,
      });
    } catch (error) {
      if (
        error instanceof Prisma.PrismaClientKnownRequestError &&
        error.code === 'P2002'
      ) {
        throw new ConflictException(MESSAGES.DEVICE.CATALOG_MODEL_DUPLICATE);
      }
      throw error;
    }
  }

  /**
   * 删除植入物目录：若已被患者手术引用，则返回 409。
   */
  async removeCatalog(actor: ActorContext, id: number) {
    this.assertSystemAdmin(actor);
    const current = await this.findWritableCatalog(id);

    try {
      return await this.prisma.implantCatalog.delete({
        where: { id: current.id },
        select: CATALOG_SELECT,
      });
    } catch (error) {
      if (
        error instanceof Prisma.PrismaClientKnownRequestError &&
        (error.code === 'P2003' || error.code === 'P2014')
      ) {
        throw new ConflictException(MESSAGES.DEVICE.CATALOG_DELETE_CONFLICT);
      }
      throw error;
    }
  }

  /**
   * 构造列表筛选：支持按医院、患者、状态和关键词组合查询。
   */
  private buildListWhere(query: DeviceQueryDto, scopedHospitalId?: number) {
    const andConditions: Prisma.DeviceWhereInput[] = [];
    const keyword = query.keyword?.trim();

    if (scopedHospitalId != null) {
      andConditions.push({
        patient: {
          is: {
            hospitalId: scopedHospitalId,
          },
        },
      });
    }

    if (query.patientId != null) {
      andConditions.push({
        patientId: query.patientId,
      });
    }

    if (query.status != null) {
      andConditions.push({
        status: query.status,
      });
    }

    if (keyword) {
      andConditions.push({
        OR: [
          {
            implantModel: {
              contains: keyword,
              mode: 'insensitive',
            },
          },
          {
            implantName: {
              contains: keyword,
              mode: 'insensitive',
            },
          },
          {
            patient: {
              is: {
                name: {
                  contains: keyword,
                  mode: 'insensitive',
                },
              },
            },
          },
          {
            patient: {
              is: {
                phone: {
                  contains: keyword,
                },
              },
            },
          },
        ],
      });
    }

    return andConditions.length > 0 ? { AND: andConditions } : {};
  }

  /**
   * 构造型号字典查询条件。
   */
  private buildCatalogWhere(keyword?: string): Prisma.ImplantCatalogWhereInput {
    const andConditions: Prisma.ImplantCatalogWhereInput[] = [];
    const normalizedKeyword = keyword?.trim();

    if (normalizedKeyword) {
      andConditions.push({
        OR: [
          {
            modelCode: {
              contains: normalizedKeyword,
              mode: 'insensitive',
            },
          },
          {
            manufacturer: {
              contains: normalizedKeyword,
              mode: 'insensitive',
            },
          },
          {
            name: {
              contains: normalizedKeyword,
              mode: 'insensitive',
            },
          },
          {
            notes: {
              contains: normalizedKeyword,
              mode: 'insensitive',
            },
          },
        ],
      });
    }

    return andConditions.length > 0 ? { AND: andConditions } : {};
  }

  /**
   * 解析列表分页。
   */
  private resolvePaging(query: DeviceQueryDto) {
    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,
    };
  }

  /**
   * 解析当前查询实际生效的医院作用域。
   */
  private resolveScopedHospitalId(
    actor: ActorContext,
    hospitalId?: number,
  ): number | undefined {
    if (actor.role === Role.SYSTEM_ADMIN) {
      return hospitalId;
    }

    return this.requireActorHospitalId(actor);
  }

  /**
   * 读取并校验当前管理员可写的患者。
   */
  private async resolveWritablePatient(actor: ActorContext, patientId: number) {
    const normalizedPatientId = this.toInt(
      patientId,
      MESSAGES.DEVICE.PATIENT_REQUIRED,
    );

    const patient = await this.prisma.patient.findUnique({
      where: { id: normalizedPatientId },
      select: {
        id: true,
        hospitalId: true,
      },
    });

    if (!patient) {
      throw new NotFoundException(MESSAGES.DEVICE.PATIENT_NOT_FOUND);
    }

    if (
      actor.role === Role.HOSPITAL_ADMIN &&
      patient.hospitalId !== this.requireActorHospitalId(actor)
    ) {
      throw new ForbiddenException(MESSAGES.DEVICE.PATIENT_SCOPE_FORBIDDEN);
    }

    return patient;
  }

  /**
   * 查询当前管理员可写的型号字典。
   */
  private async findWritableCatalog(id: number) {
    const catalogId = this.toInt(id, 'id');
    const catalog = await this.prisma.implantCatalog.findUnique({
      where: { id: catalogId },
      select: CATALOG_SELECT,
    });

    if (!catalog) {
      throw new NotFoundException(MESSAGES.DEVICE.CATALOG_NOT_FOUND);
    }

    return catalog;
  }

  /**
   * 校验当前用户是否可读/写该设备。
   */
  private assertDeviceReadable(actor: ActorContext, hospitalId: number) {
    if (actor.role === Role.SYSTEM_ADMIN) {
      return;
    }

    if (hospitalId !== this.requireActorHospitalId(actor)) {
      throw new ForbiddenException(MESSAGES.DEFAULT_FORBIDDEN);
    }
  }

  /**
   * 管理员角色校验：仅系统管理员与院管可操作患者植入实例。
   */
  private async findRemovableDevice(actor: ActorContext, id: number) {
    const deviceId = this.toInt(id, 'id');
    const device = await this.prisma.device.findUnique({
      where: { id: deviceId },
      select: {
        id: true,
        patient: {
          select: {
            hospitalId: true,
            doctorId: true,
            doctor: {
              select: {
                departmentId: true,
                groupId: true,
              },
            },
          },
        },
      },
    });

    if (!device) {
      throw new NotFoundException(MESSAGES.DEVICE.NOT_FOUND);
    }

    this.assertDeviceRemovableScope(actor, device.patient);
    return device;
  }

  private assertDeviceRemovableScope(
    actor: ActorContext,
    patient: {
      hospitalId: number;
      doctorId: number;
      doctor: { departmentId: number | null; groupId: number | null };
    },
  ) {
    switch (actor.role) {
      case Role.SYSTEM_ADMIN:
        return;
      case Role.HOSPITAL_ADMIN:
        if (patient.hospitalId !== this.requireActorHospitalId(actor)) {
          throw new ForbiddenException(MESSAGES.DEFAULT_FORBIDDEN);
        }
        return;
      case Role.DIRECTOR:
        if (
          !actor.departmentId ||
          patient.doctor.departmentId !== actor.departmentId
        ) {
          throw new ForbiddenException(MESSAGES.DEFAULT_FORBIDDEN);
        }
        return;
      case Role.LEADER:
        if (!actor.groupId || patient.doctor.groupId !== actor.groupId) {
          throw new ForbiddenException(MESSAGES.DEFAULT_FORBIDDEN);
        }
        return;
      case Role.DOCTOR:
        if (patient.doctorId !== actor.id) {
          throw new ForbiddenException(MESSAGES.DEFAULT_FORBIDDEN);
        }
        return;
      default:
        throw new ForbiddenException(MESSAGES.DEFAULT_FORBIDDEN);
    }
  }

  private assertAdmin(actor: ActorContext) {
    if (
      actor.role !== Role.SYSTEM_ADMIN &&
      actor.role !== Role.HOSPITAL_ADMIN
    ) {
      throw new ForbiddenException(MESSAGES.DEFAULT_FORBIDDEN);
    }
  }

  /**
   * 型号字典读权限：B 端全部已登录角色可访问。
   */
  private assertCatalogReadable(actor: ActorContext) {
    if (
      actor.role === Role.SYSTEM_ADMIN ||
      actor.role === Role.HOSPITAL_ADMIN ||
      actor.role === Role.DIRECTOR ||
      actor.role === Role.LEADER ||
      actor.role === Role.DOCTOR ||
      actor.role === Role.ENGINEER
    ) {
      return;
    }

    throw new ForbiddenException(MESSAGES.DEFAULT_FORBIDDEN);
  }

  /**
   * 全局植入物目录仅系统管理员可维护。
   */
  private assertSystemAdmin(actor: ActorContext) {
    if (actor.role !== Role.SYSTEM_ADMIN) {
      throw new ForbiddenException(MESSAGES.DEFAULT_FORBIDDEN);
    }
  }

  /**
   * 型号编码标准化：统一去空白并转大写。
   */
  private normalizeModelCode(value: unknown) {
    return this.normalizeRequiredString(value, 'modelCode').toUpperCase();
  }

  private normalizeRequiredString(value: unknown, fieldName: string) {
    if (typeof value !== 'string') {
      throw new BadRequestException(`${fieldName} 必须是字符串`);
    }

    const normalized = value.trim();
    if (!normalized) {
      throw new BadRequestException(`${fieldName} 不能为空`);
    }
    return normalized;
  }

  private normalizeNullableString(value: unknown, fieldName: string) {
    if (typeof value !== 'string') {
      throw new BadRequestException(`${fieldName} 必须是字符串`);
    }

    const normalized = value.trim();
    return normalized || null;
  }

  /**
   * 挡位列表标准化：去重、排序，并在非可调压目录下自动清空。
   */
  private normalizePressureLevels(
    pressureLevels: unknown[] | undefined,
    isValve: boolean,
  ) {
    if (!isValve) {
      return [];
    }

    const normalized = normalizePressureLabelList(
      pressureLevels,
      'pressureLevels',
    );
    if (normalized.length === 0) {
      throw new BadRequestException(MESSAGES.DEVICE.VALVE_PRESSURE_REQUIRED);
    }

    return normalized;
  }

  /**
   * 当前压力挡位标签标准化。
   */
  private normalizePressure(value: unknown) {
    try {
      return normalizePressureLabel(value, 'currentPressure');
    } catch {
      throw new BadRequestException(MESSAGES.DEVICE.CURRENT_PRESSURE_INVALID);
    }
  }

  /**
   * 设备状态枚举校验。
   */
  private normalizeStatus(value: unknown): DeviceStatus {
    if (!Object.values(DeviceStatus).includes(value as DeviceStatus)) {
      throw new BadRequestException(MESSAGES.DEVICE.STATUS_INVALID);
    }
    return value as DeviceStatus;
  }

  /**
   * 统一整数参数校验。
   */
  private toInt(value: unknown, message: string) {
    const parsed = Number(value);
    if (!Number.isInteger(parsed) || parsed <= 0) {
      throw new BadRequestException(message);
    }
    return parsed;
  }

  /**
   * 当前登录上下文中的医院 ID 对院管是必填项。
   */
  private requireActorHospitalId(actor: ActorContext) {
    if (
      typeof actor.hospitalId !== 'number' ||
      !Number.isInteger(actor.hospitalId) ||
      actor.hospitalId <= 0
    ) {
      throw new BadRequestException(MESSAGES.DEVICE.ACTOR_HOSPITAL_REQUIRED);
    }
    return actor.hospitalId;
  }
}
