import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
import { AuditService } from '../../audit/audit.service';
import { PrismaService } from '../../prisma/prisma.service';

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

  async listar(empresaId: string, ejercicioId?: string) {
    return this.prisma.cont_periodos.findMany({
      where: {
        empresa_id: empresaId,
        ...(ejercicioId ? { ejercicio_id: ejercicioId } : {}),
      },
      include: {
        ejercicio: { select: { anio: true } },
        _count: { select: { asientos: true } },
      },
      orderBy: [{ anio: 'desc' }, { numero: 'asc' }],
    });
  }

  async getPeriodoActivo(empresaId: string): Promise<string | null> {
    const hoy = new Date();
    const periodo = await this.prisma.cont_periodos.findFirst({
      where: {
        empresa_id: empresaId,
        estado: { in: ['ABIERTO', 'AJUSTE'] },
        fecha_inicio: { lte: hoy },
        fecha_fin: { gte: hoy },
      },
      orderBy: { numero: 'asc' },
    });
    return periodo?.id ?? null;
  }

  async resolverPeriodoPorFecha(empresaId: string, fecha: Date): Promise<string> {
    const periodo = await this.prisma.cont_periodos.findFirst({
      where: {
        empresa_id: empresaId,
        estado: { in: ['ABIERTO', 'AJUSTE'] },
        fecha_inicio: { lte: fecha },
        fecha_fin: { gte: fecha },
      },
    });
    if (!periodo) throw new BadRequestException(`No existe un período contable abierto para la fecha ${fecha.toISOString().substring(0, 10)}`);
    return periodo.id;
  }

  async cerrar(periodoId: string, empresaId: string, diasAjuste?: number, usuarioId?: string) {
    const periodo = await this.prisma.cont_periodos.findFirst({
      where: { id: periodoId, empresa_id: empresaId },
    });
    if (!periodo) throw new NotFoundException('Período no encontrado');
    if (periodo.estado === 'CERRADO' || periodo.estado === 'BLOQUEADO') {
      throw new BadRequestException(`El período ya está en estado ${periodo.estado}`);
    }

    const nuevoEstado = diasAjuste && diasAjuste > 0 ? 'AJUSTE' : 'CERRADO';

    const result = await this.prisma.cont_periodos.update({
      where: { id: periodoId },
      data: {
        estado: nuevoEstado,
        dias_ajuste: diasAjuste ?? null,
        fecha_cierre: new Date(),
        updated_at: new Date(),
      },
    });
    this.audit.log({ empresa_id: empresaId, user_id: usuarioId, action: 'CLOSE', entity_type: 'cont_periodos', entity_id: periodoId, descripcion: `Período ${periodo.mes}/${periodo.anio} cerrado (${nuevoEstado})` });
    return result;
  }

  async reabrir(periodoId: string, empresaId: string, usuarioId?: string) {
    const periodo = await this.prisma.cont_periodos.findFirst({
      where: { id: periodoId, empresa_id: empresaId },
    });
    if (!periodo) throw new NotFoundException('Período no encontrado');
    if (periodo.estado === 'BLOQUEADO') throw new BadRequestException('El período está bloqueado y no puede reabrirse');
    if (periodo.estado === 'ABIERTO') throw new BadRequestException('El período ya está abierto');

    const result = await this.prisma.cont_periodos.update({
      where: { id: periodoId },
      data: { estado: 'ABIERTO', dias_ajuste: null, fecha_cierre: null, updated_at: new Date() },
    });
    this.audit.log({ empresa_id: empresaId, user_id: usuarioId, action: 'REOPEN', entity_type: 'cont_periodos', entity_id: periodoId, descripcion: `Período ${periodo.mes}/${periodo.anio} reabierto` });
    return result;
  }

  async bloquear(periodoId: string, empresaId: string, usuarioId?: string) {
    const periodo = await this.prisma.cont_periodos.findFirst({
      where: { id: periodoId, empresa_id: empresaId },
    });
    if (!periodo) throw new NotFoundException('Período no encontrado');

    const result = await this.prisma.cont_periodos.update({
      where: { id: periodoId },
      data: { estado: 'BLOQUEADO', updated_at: new Date() },
    });
    this.audit.log({ empresa_id: empresaId, user_id: usuarioId, action: 'BLOCK', entity_type: 'cont_periodos', entity_id: periodoId, descripcion: `Período ${periodo.mes}/${periodo.anio} bloqueado` });
    return result;
  }

  async validarPeriodoAbierto(periodoId: string, empresaId: string, esAdmin = false) {
    const periodo = await this.prisma.cont_periodos.findFirst({
      where: { id: periodoId, empresa_id: empresaId },
    });
    if (!periodo) throw new NotFoundException('Período no encontrado');

    if (periodo.estado === 'BLOQUEADO') throw new BadRequestException('El período está bloqueado. No se pueden registrar asientos');
    if (periodo.estado === 'CERRADO') throw new BadRequestException('El período está cerrado. No se pueden registrar asientos');
    if (periodo.estado === 'AJUSTE' && !esAdmin) throw new BadRequestException('El período está en período de ajuste. Solo administradores pueden registrar asientos');
  }
}
