import {
  BadRequestException,
  ConflictException,
  Injectable,
  NotFoundException,
} from '@nestjs/common';
import { Prisma } from '@prisma/client';
import { PrismaService } from '../../prisma/prisma.service';
import {
  CreateToleranciaEspecialDto,
  QueryToleranciasEspecialesDto,
  ToleranciaAplicaA,
  UpdateToleranciaEspecialDto,
} from '../dto/presentismo-fase3.dto';

@Injectable()
export class ToleranciasPresentismoService {
  constructor(private readonly prisma: PrismaService) {}

  async listar(empresaId: string, query: QueryToleranciasEspecialesDto) {
    const page = query.page ?? 1;
    const limit = query.limit ?? 50;
    const skip = (page - 1) * limit;

    const where: Prisma.rrhh_presentismo_tolerancias_especialesWhereInput = {
      empresa_id: empresaId,
    };
    if (query.aplica_a) where.aplica_a = query.aplica_a as any;
    if (query.fecha) where.fecha = new Date(query.fecha);

    const [data, total] = await this.prisma.$transaction([
      this.prisma.rrhh_presentismo_tolerancias_especiales.findMany({
        where,
        skip,
        take: limit,
        orderBy: [{ fecha: 'desc' }, { created_at: 'desc' }],
      }),
      this.prisma.rrhh_presentismo_tolerancias_especiales.count({ where }),
    ]);

    return { data, total, page, limit };
  }

  async obtener(id: string, empresaId: string) {
    const row = await this.prisma.rrhh_presentismo_tolerancias_especiales.findFirst({
      where: { id, empresa_id: empresaId },
    });
    if (!row) throw new NotFoundException('Tolerancia especial no encontrada');
    return row;
  }

  async crear(empresaId: string, dto: CreateToleranciaEspecialDto, userId?: string) {
    await this.validarScope(empresaId, dto.aplica_a ?? ToleranciaAplicaA.TODOS, dto.aplica_a_id);

    const existe = await this.prisma.rrhh_presentismo_tolerancias_especiales.findFirst({
      where: {
        empresa_id: empresaId,
        fecha: new Date(dto.fecha),
        aplica_a: (dto.aplica_a ?? ToleranciaAplicaA.TODOS) as any,
        aplica_a_id: dto.aplica_a_id ?? null,
      },
      select: { id: true },
    });
    if (existe) {
      throw new ConflictException(
        'Ya existe una tolerancia especial para la misma fecha y alcance; edite la existente.',
      );
    }

    return this.prisma.rrhh_presentismo_tolerancias_especiales.create({
      data: {
        empresa_id: empresaId,
        fecha: new Date(dto.fecha),
        tolerancia_entrada_min: dto.tolerancia_entrada_min,
        tolerancia_salida_min: dto.tolerancia_salida_min,
        motivo: dto.motivo,
        aplica_a: (dto.aplica_a ?? ToleranciaAplicaA.TODOS) as any,
        aplica_a_id: dto.aplica_a_id ?? null,
        creado_por: userId ?? null,
      },
    });
  }

  async actualizar(id: string, empresaId: string, dto: UpdateToleranciaEspecialDto) {
    const actual = await this.prisma.rrhh_presentismo_tolerancias_especiales.findFirst({
      where: { id, empresa_id: empresaId },
    });
    if (!actual) throw new NotFoundException('Tolerancia especial no encontrada');

    const aplicaA = dto.aplica_a ?? (actual.aplica_a as any);
    const aplicaAId = dto.aplica_a_id ?? actual.aplica_a_id ?? undefined;
    await this.validarScope(empresaId, aplicaA, aplicaAId);

    const fecha = dto.fecha ? new Date(dto.fecha) : actual.fecha;
    const existe = await this.prisma.rrhh_presentismo_tolerancias_especiales.findFirst({
      where: {
        empresa_id: empresaId,
        fecha,
        aplica_a: aplicaA as any,
        aplica_a_id: aplicaAId ?? null,
        id: { not: id },
      },
      select: { id: true },
    });
    if (existe) {
      throw new ConflictException(
        'Ya existe otra tolerancia especial para la misma fecha y alcance.',
      );
    }

    return this.prisma.rrhh_presentismo_tolerancias_especiales.update({
      where: { id },
      data: {
        fecha: dto.fecha ? new Date(dto.fecha) : undefined,
        tolerancia_entrada_min: dto.tolerancia_entrada_min ?? undefined,
        tolerancia_salida_min: dto.tolerancia_salida_min ?? undefined,
        motivo: dto.motivo ?? undefined,
        aplica_a: dto.aplica_a ? (dto.aplica_a as any) : undefined,
        aplica_a_id: dto.aplica_a_id !== undefined ? dto.aplica_a_id || null : undefined,
      },
    });
  }

  async eliminar(id: string, empresaId: string) {
    const actual = await this.prisma.rrhh_presentismo_tolerancias_especiales.findFirst({
      where: { id, empresa_id: empresaId },
      select: { id: true },
    });
    if (!actual) throw new NotFoundException('Tolerancia especial no encontrada');

    await this.prisma.rrhh_presentismo_tolerancias_especiales.delete({ where: { id } });
    return { eliminado: true };
  }

  private async validarScope(empresaId: string, aplicaA: ToleranciaAplicaA, aplicaAId?: string) {
    if (aplicaA === ToleranciaAplicaA.TODOS) {
      if (aplicaAId) {
        throw new BadRequestException('aplica_a_id debe ser null cuando aplica_a = TODOS');
      }
      return;
    }

    if (!aplicaAId) {
      throw new BadRequestException('aplica_a_id es obligatorio cuando aplica_a != TODOS');
    }

    if (aplicaA === ToleranciaAplicaA.SUCURSAL) {
      const suc = await this.prisma.empresas_sucursales.findFirst({
        where: { id: aplicaAId, empresa_id: empresaId },
        select: { id: true },
      });
      if (!suc) throw new BadRequestException('aplica_a_id no corresponde a una sucursal de la empresa');
      return;
    }

    if (aplicaA === ToleranciaAplicaA.DEPARTAMENTO) {
      const dep = await this.prisma.rrhh_departamentos.findFirst({
        where: { id: aplicaAId, empresa_id: empresaId },
        select: { id: true },
      });
      if (!dep) {
        throw new BadRequestException('aplica_a_id no corresponde a un departamento de la empresa');
      }
    }
  }
}
