import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../../prisma/prisma.service';
import {
  UpsertCargoDto,
  UpsertCentroCostoDto,
  UpsertDepartamentoDto,
  UpsertTipoEmpleadoDto,
} from '../dto/catalogos.dto';

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

  // ───── Bancos disponibles (empresa + globales) ───────────
  listarBancos(empresaId: string) {
    return this.prisma.bancos.findMany({
      where: { OR: [{ empresa_id: empresaId }, { empresa_id: null }], activo: true },
      orderBy: [{ descripcion: 'asc' }],
      select: { id: true, codigo: true, descripcion: true },
    });
  }

  // ───── Cargos ─────────────────────────────────────────────
  listarCargos(empresaId: string) {
    return this.prisma.rrhh_cargos.findMany({
      where: { empresa_id: empresaId },
      orderBy: { nombre: 'asc' },
    });
  }

  crearCargo(empresaId: string, dto: UpsertCargoDto) {
    return this.prisma.rrhh_cargos.create({
      data: { ...dto, empresa_id: empresaId, activo: dto.activo ?? true },
    });
  }

  async actualizarCargo(id: string, empresaId: string, dto: UpsertCargoDto) {
    const existe = await this.prisma.rrhh_cargos.findFirst({ where: { id, empresa_id: empresaId } });
    if (!existe) throw new NotFoundException('Cargo no encontrado');
    return this.prisma.rrhh_cargos.update({
      where: { id },
      data: { ...dto, updated_at: new Date() },
    });
  }

  async eliminarCargo(id: string, empresaId: string) {
    const existe = await this.prisma.rrhh_cargos.findFirst({ where: { id, empresa_id: empresaId } });
    if (!existe) throw new NotFoundException('Cargo no encontrado');
    const enUso = await this.prisma.rrhh_empleados.findFirst({ where: { cargo_id: id } });
    if (enUso) throw new BadRequestException('El cargo está asignado a empleados y no puede eliminarse');
    return this.prisma.rrhh_cargos.delete({ where: { id } });
  }

  // ───── Centros de costo ───────────────────────────────────
  // Unificados con Contabilidad: viven en cont_centros_costo (campo `descripcion`).
  // Estos endpoints exponen el campo como `nombre` por compatibilidad con el frontend de RRHH.
  private mapCentroCosto(cc: { id: string; empresa_id: string; codigo: string; descripcion: string; activo: boolean; created_at: Date | null; updated_at: Date | null }) {
    return { ...cc, nombre: cc.descripcion };
  }

  async listarCentrosCosto(empresaId: string) {
    const rows = await this.prisma.cont_centros_costo.findMany({
      where: { empresa_id: empresaId },
      orderBy: { codigo: 'asc' },
    });
    return rows.map((r) => this.mapCentroCosto(r));
  }

  async crearCentroCosto(empresaId: string, dto: UpsertCentroCostoDto) {
    const dup = await this.prisma.cont_centros_costo.findFirst({
      where: { empresa_id: empresaId, codigo: dto.codigo },
    });
    if (dup) throw new BadRequestException(`Ya existe un centro de costo con código ${dto.codigo}`);
    const cc = await this.prisma.cont_centros_costo.create({
      data: { empresa_id: empresaId, codigo: dto.codigo, descripcion: dto.nombre, activo: dto.activo ?? true },
    });
    return this.mapCentroCosto(cc);
  }

  async actualizarCentroCosto(id: string, empresaId: string, dto: UpsertCentroCostoDto) {
    const existe = await this.prisma.cont_centros_costo.findFirst({ where: { id, empresa_id: empresaId } });
    if (!existe) throw new NotFoundException('Centro de costo no encontrado');
    if (dto.codigo && dto.codigo !== existe.codigo) {
      const dup = await this.prisma.cont_centros_costo.findFirst({
        where: { empresa_id: empresaId, codigo: dto.codigo, NOT: { id } },
      });
      if (dup) throw new BadRequestException(`Ya existe un centro de costo con código ${dto.codigo}`);
    }
    const cc = await this.prisma.cont_centros_costo.update({
      where: { id },
      data: { codigo: dto.codigo, descripcion: dto.nombre, activo: dto.activo, updated_at: new Date() },
    });
    return this.mapCentroCosto(cc);
  }

  async eliminarCentroCosto(id: string, empresaId: string) {
    const existe = await this.prisma.cont_centros_costo.findFirst({ where: { id, empresa_id: empresaId } });
    if (!existe) throw new NotFoundException('Centro de costo no encontrado');
    const enUsoEmpleado = await this.prisma.rrhh_empleados.findFirst({ where: { centro_costo_id: id } });
    if (enUsoEmpleado) throw new BadRequestException('El centro de costo está en uso por empleados');
    const enUsoAsiento = await this.prisma.cont_asientos_det.findFirst({ where: { centro_costo_id: id } });
    if (enUsoAsiento) throw new BadRequestException('El centro de costo está en uso por asientos contables');
    return this.mapCentroCosto(await this.prisma.cont_centros_costo.delete({ where: { id } }));
  }

  // ───── Departamentos ──────────────────────────────────────
  listarDepartamentos(empresaId: string) {
    return this.prisma.rrhh_departamentos.findMany({
      where: { empresa_id: empresaId },
      orderBy: { nombre: 'asc' },
      include: {
        sucursal: { select: { id: true, descripcion: true } },
        responsable: { select: { id: true, nombres: true, apellidos: true } },
      },
    });
  }

  async crearDepartamento(empresaId: string, dto: UpsertDepartamentoDto) {
    const sucursal = await this.prisma.empresas_sucursales.findFirst({
      where: { id: dto.sucursal_id, empresa_id: empresaId },
    });
    if (!sucursal) throw new BadRequestException('Sucursal inválida para esta empresa');
    return this.prisma.rrhh_departamentos.create({
      data: { ...dto, empresa_id: empresaId, activo: dto.activo ?? true },
    });
  }

  async actualizarDepartamento(id: string, empresaId: string, dto: UpsertDepartamentoDto) {
    const existe = await this.prisma.rrhh_departamentos.findFirst({ where: { id, empresa_id: empresaId } });
    if (!existe) throw new NotFoundException('Departamento no encontrado');
    if (dto.sucursal_id && dto.sucursal_id !== existe.sucursal_id) {
      const sucursal = await this.prisma.empresas_sucursales.findFirst({
        where: { id: dto.sucursal_id, empresa_id: empresaId },
      });
      if (!sucursal) throw new BadRequestException('Sucursal inválida para esta empresa');
    }
    return this.prisma.rrhh_departamentos.update({
      where: { id },
      data: { ...dto, updated_at: new Date() },
    });
  }

  async eliminarDepartamento(id: string, empresaId: string) {
    const existe = await this.prisma.rrhh_departamentos.findFirst({ where: { id, empresa_id: empresaId } });
    if (!existe) throw new NotFoundException('Departamento no encontrado');
    const enUso = await this.prisma.rrhh_empleados.findFirst({ where: { departamento_id: id } });
    if (enUso) throw new BadRequestException('El departamento tiene empleados asignados');
    return this.prisma.rrhh_departamentos.delete({ where: { id } });
  }

  // ───── Tipos de empleado ──────────────────────────────────
  listarTiposEmpleado(empresaId: string) {
    return this.prisma.rrhh_tipos_empleado.findMany({
      where: { empresa_id: empresaId },
      orderBy: { nombre: 'asc' },
    });
  }

  async crearTipoEmpleado(empresaId: string, dto: UpsertTipoEmpleadoDto) {
    const dup = await this.prisma.rrhh_tipos_empleado.findFirst({
      where: { empresa_id: empresaId, codigo: dto.codigo },
    });
    if (dup) throw new BadRequestException(`Ya existe un tipo de empleado con código ${dto.codigo}`);
    return this.prisma.rrhh_tipos_empleado.create({
      data: {
        ...dto,
        empresa_id: empresaId,
        recibe_aguinaldo: dto.recibe_aguinaldo ?? true,
        activo: dto.activo ?? true,
      },
    });
  }

  async actualizarTipoEmpleado(id: string, empresaId: string, dto: UpsertTipoEmpleadoDto) {
    const existe = await this.prisma.rrhh_tipos_empleado.findFirst({ where: { id, empresa_id: empresaId } });
    if (!existe) throw new NotFoundException('Tipo de empleado no encontrado');
    if (dto.codigo && dto.codigo !== existe.codigo) {
      const dup = await this.prisma.rrhh_tipos_empleado.findFirst({
        where: { empresa_id: empresaId, codigo: dto.codigo, NOT: { id } },
      });
      if (dup) throw new BadRequestException(`Ya existe un tipo de empleado con código ${dto.codigo}`);
    }
    return this.prisma.rrhh_tipos_empleado.update({
      where: { id },
      data: { ...dto, updated_at: new Date() },
    });
  }

  async eliminarTipoEmpleado(id: string, empresaId: string) {
    const existe = await this.prisma.rrhh_tipos_empleado.findFirst({ where: { id, empresa_id: empresaId } });
    if (!existe) throw new NotFoundException('Tipo de empleado no encontrado');
    const enUso = await this.prisma.rrhh_empleados.findFirst({ where: { tipo_empleado_id: id } });
    if (enUso) throw new BadRequestException('El tipo de empleado está en uso');
    return this.prisma.rrhh_tipos_empleado.delete({ where: { id } });
  }
}
