import {
  ConflictException,
  Injectable,
  NotFoundException,
} from '@nestjs/common';
import { AuditAction, AuditEntityType } from '@aechr/shared';
import { Prisma } from '@prisma/client';
import type { JwtAuthUser } from '../../common/interfaces/jwt-auth-user.interface';
import {
  buildPagination,
  buildSoftDeleteWhere,
} from '../../common/prisma/pagination';
import { AuditService } from '../audit/audit.service';
import { PrismaService } from '../prisma/prisma.service';
import { CreateDepartmentDto } from './dto/create-department.dto';
import { DepartmentQueryDto } from './dto/department-query.dto';
import { UpdateDepartmentDto } from './dto/update-department.dto';

@Injectable()
export class DepartmentsService {
  constructor(
    private readonly prisma: PrismaService,
    private readonly auditService: AuditService,
  ) {}

  async list(query: DepartmentQueryDto) {
    const { skip, take, meta } = buildPagination(query);
    const where: Prisma.DepartmentWhereInput = {
      ...buildSoftDeleteWhere(query.includeDeleted),
      ...(query.search
        ? {
            OR: [
              { name: { contains: query.search } },
              { code: { contains: query.search } },
            ],
          }
        : {}),
    };

    const [items, total] = await this.prisma.$transaction([
      this.prisma.department.findMany({
        where,
        skip,
        take,
        orderBy: { createdAt: query.sortOrder },
      }),
      this.prisma.department.count({ where }),
    ]);

    return { items, total, ...meta };
  }

  async create(dto: CreateDepartmentDto, actor: JwtAuthUser) {
    const existing = await this.prisma.department.findFirst({
      where: { OR: [{ name: dto.name }, { code: dto.code }] },
    });
    if (existing && !existing.deletedAt) {
      throw new ConflictException('Department already exists');
    }

    const department = await this.prisma.department.create({
      data: {
        ...dto,
        createdBy: actor.sub,
        updatedBy: actor.sub,
      },
    });

    await this.auditService.log({
      module: 'departments',
      action: AuditAction.CREATE,
      entityType: AuditEntityType.DEPARTMENT,
      entityId: department.id,
      newValues: dto as unknown as Prisma.InputJsonValue,
      createdBy: actor.sub,
      updatedBy: actor.sub,
    });

    return department;
  }

  async findOne(id: string) {
    const department = await this.prisma.department.findUnique({ where: { id } });
    if (!department) {
      throw new NotFoundException('Department not found');
    }
    return department;
  }

  async update(id: string, dto: UpdateDepartmentDto, actor: JwtAuthUser) {
    const department = await this.findOne(id);

    const updated = await this.prisma.department.update({
      where: { id },
      data: {
        ...dto,
        updatedBy: actor.sub,
      },
    });

    await this.auditService.log({
      module: 'departments',
      action: AuditAction.UPDATE,
      entityType: AuditEntityType.DEPARTMENT,
      entityId: id,
      oldValues: {
        name: department.name,
        code: department.code,
        description: department.description,
      } as Prisma.InputJsonValue,
      newValues: dto as Prisma.InputJsonValue,
      createdBy: actor.sub,
      updatedBy: actor.sub,
    });

    return updated;
  }

  async remove(id: string, actor: JwtAuthUser) {
    await this.findOne(id);
    const deleted = await this.prisma.department.update({
      where: { id },
      data: {
        deletedAt: new Date(),
        deletedBy: actor.sub,
        updatedBy: actor.sub,
      },
    });

    await this.auditService.log({
      module: 'departments',
      action: AuditAction.DELETE,
      entityType: AuditEntityType.DEPARTMENT,
      entityId: id,
      remarks: 'Department soft deleted',
      createdBy: actor.sub,
      updatedBy: actor.sub,
      deletedBy: actor.sub,
    });

    return deleted;
  }

  async restore(id: string, actor: JwtAuthUser) {
    await this.findOne(id);
    const restored = await this.prisma.department.update({
      where: { id },
      data: {
        deletedAt: null,
        deletedBy: null,
        updatedBy: actor.sub,
      },
    });

    await this.auditService.log({
      module: 'departments',
      action: AuditAction.RESTORE,
      entityType: AuditEntityType.DEPARTMENT,
      entityId: id,
      remarks: 'Department restored',
      createdBy: actor.sub,
      updatedBy: actor.sub,
    });

    return restored;
  }
}
