import { BadRequestException, ConflictException, Injectable, NotFoundException } from '@nestjs/common';
import { Prisma } from '@prisma/client';
import { PrismaService } from '../../prisma/prisma.service';
import {
  CreateNovedadDto,
  QueryNovedadesDto,
  UpdateNovedadDto,
} from '../dto/novedades.dto';

const parseHora = (value?: string | null): Date | undefined => {
  if (!value) return undefined;
  const [hh, mm, ss = '0'] = String(value).split(':');
  const d = new Date('1970-01-01T00:00:00Z');
  d.setUTCHours(Number(hh) || 0, Number(mm) || 0, Number(ss) || 0, 0);
  return d;
};

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

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

    const where: Prisma.rrhh_asistencia_novedadesWhereInput = { empresa_id: empresaId };
    if (query.tipo_novedad) where.tipo_novedad = query.tipo_novedad as any;
    if (query.empleado_id) where.empleado_id = query.empleado_id;
    if (typeof query.justificado === 'boolean') where.justificado = query.justificado;
    if (typeof query.impacta_liquidacion === 'boolean')
      where.impacta_liquidacion = query.impacta_liquidacion;
    if (typeof query.generado_automatico === 'boolean')
      where.generado_automatico = query.generado_automatico;
    if (query.estado) where.estado = query.estado as any;
    if (query.fecha_desde || query.fecha_hasta) {
      where.fecha = {};
      if (query.fecha_desde) (where.fecha as any).gte = new Date(query.fecha_desde);
      if (query.fecha_hasta) (where.fecha as any).lte = new Date(query.fecha_hasta);
    }

    const [data, total] = await this.prisma.$transaction([
      this.prisma.rrhh_asistencia_novedades.findMany({
        where,
        skip,
        take: limit,
        orderBy: [{ fecha: 'desc' }, { created_at: 'desc' }],
        include: {
          empleado: {
            select: {
              id: true,
              numero_empleado: true,
              nombres: true,
              apellidos: true,
            },
          },
          turno: { select: { id: true, nombre: true, tipo: true } },
          permiso: { select: { id: true, tipo_permiso: true, estado: true } },
        },
      }),
      this.prisma.rrhh_asistencia_novedades.count({ where }),
    ]);

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

  async crear(empresaId: string, dto: CreateNovedadDto, userId?: string) {
    const empleado = await this.prisma.rrhh_empleados.findFirst({
      where: { id: dto.empleado_id, empresa_id: empresaId },
    });
    if (!empleado) throw new BadRequestException('Empleado inválido para esta empresa');

    return this.prisma.rrhh_asistencia_novedades.create({
      data: {
        empresa_id: empresaId,
        empleado_id: dto.empleado_id,
        fecha: new Date(dto.fecha),
        tipo_novedad: dto.tipo_novedad,
        hora_entrada_real: parseHora(dto.hora_entrada_real),
        hora_entrada_esperada: parseHora(dto.hora_entrada_esperada),
        minutos_tardanza: dto.minutos_tardanza ?? null,
        justificado: dto.justificado ?? false,
        tipo_justificacion: dto.tipo_justificacion,
        impacta_liquidacion: dto.impacta_liquidacion ?? true,
        observacion: dto.observacion,
        creado_por: userId,
      },
    });
  }

  async actualizar(id: string, empresaId: string, dto: UpdateNovedadDto) {
    const novedad = await this.prisma.rrhh_asistencia_novedades.findFirst({
      where: { id, empresa_id: empresaId },
    });
    if (!novedad) throw new NotFoundException('Novedad no encontrada');
    if (novedad.liquidacion_id) {
      throw new BadRequestException(
        'La novedad ya fue aplicada a una liquidación cerrada y no puede modificarse',
      );
    }

    return this.prisma.$transaction(async (tx) => {
      const updated = await tx.rrhh_asistencia_novedades.update({
        where: { id },
        data: {
          ...dto,
          fecha: dto.fecha ? new Date(dto.fecha) : undefined,
          hora_entrada_real:
            dto.hora_entrada_real !== undefined ? parseHora(dto.hora_entrada_real) : undefined,
          hora_entrada_esperada:
            dto.hora_entrada_esperada !== undefined ? parseHora(dto.hora_entrada_esperada) : undefined,
          updated_at: new Date(),
        },
      });
      if (novedad.preliquidacion_id) {
        await tx.rrhh_liquidaciones_cabecera.updateMany({
          where: { id: novedad.preliquidacion_id, estado: 'PRE_LIQUIDACION' },
          data: { recalculo_pendiente: true, updated_at: new Date() },
        });
      }
      return updated;
    });
  }

  async eliminar(id: string, empresaId: string) {
    const novedad = await this.prisma.rrhh_asistencia_novedades.findFirst({
      where: { id, empresa_id: empresaId },
    });
    if (!novedad) throw new NotFoundException('Novedad no encontrada');
    if (novedad.liquidacion_id) {
      throw new BadRequestException(
        'La novedad ya fue aplicada a una liquidación cerrada y no puede eliminarse',
      );
    }
    return this.prisma.$transaction(async (tx) => {
      const deleted = await tx.rrhh_asistencia_novedades.delete({ where: { id } });
      if (novedad.preliquidacion_id) {
        await tx.rrhh_liquidaciones_cabecera.updateMany({
          where: { id: novedad.preliquidacion_id, estado: 'PRE_LIQUIDACION' },
          data: { recalculo_pendiente: true, updated_at: new Date() },
        });
      }
      return deleted;
    });
  }

  async anularPresentismo(id: string, empresaId: string, _userId: string, motivo?: string) {
    const novedad = await this.prisma.rrhh_asistencia_novedades.findFirst({
      where: { id, empresa_id: empresaId },
    });
    if (!novedad) throw new NotFoundException('Novedad no encontrada');
    if (novedad.liquidacion_id) {
      throw new ConflictException('No se puede anular la novedad porque ya fue aplicada a liquidación cerrada. Anule la liquidación primero.');
    }
    if (novedad.estado === 'ANULADA') {
      return { anulado: false, ya_anulada: true, novedad };
    }

    const observacion =
      motivo && motivo.trim().length > 0
        ? novedad.observacion
          ? `${novedad.observacion}\n[ANULADA] ${motivo.trim()}`
          : `[ANULADA] ${motivo.trim()}`
        : novedad.observacion;

    const updated = await this.prisma.rrhh_asistencia_novedades.update({
      where: { id },
      data: {
        estado: 'ANULADA',
        observacion,
        updated_at: new Date(),
      },
    });

    return { anulado: true, ya_anulada: false, novedad: updated };
  }
}
