import {
  BadRequestException,
  Injectable,
  NotFoundException,
} from '@nestjs/common';
import { PrismaService } from '../../prisma/prisma.service';
import { CreateTesCategoriaDto } from '../dto/tes-categoria.dto';
import { UpdateTesCategoriaDto } from '../dto/tes-categoria.dto';

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

  async findAll(empresaId: string) {
    // Categorías del sistema + propias de la empresa, enriquecidas con el mapeo contable de la empresa
    const [categorias, mapeos] = await this.prisma.$transaction([
      this.prisma.tes_categorias.findMany({
        where: {
          OR: [{ is_sistema: true }, { empresa_id: empresaId }],
        },
        orderBy: [{ is_sistema: 'desc' }, { nombre: 'asc' }],
      }),
      this.prisma.tes_categoria_cuenta_contable.findMany({
        where: { empresa_id: empresaId },
      }),
    ]);

    const mapeoIndex = Object.fromEntries(
      mapeos.map((m) => [m.categoria_id, m.cuenta_contable_id]),
    );

    return categorias.map((cat) => ({
      ...cat,
      // Para categorías del sistema: cuenta_contable_id viene del mapeo de la empresa
      // Para categorías propias: viene del campo directo
      cuenta_contable_id: cat.is_sistema
        ? (mapeoIndex[cat.id] ?? null)
        : cat.cuenta_contable_id,
      mapeo_id: mapeos.find((m) => m.categoria_id === cat.id)?.id ?? null,
    }));
  }

  // ── Mapeo de cuenta contable para categorías del sistema ────────────────────

  async upsertMapeoContable(
    categoriaId: string,
    cuentaContableId: string | null,
    empresaId: string,
  ) {
    const categoria = await this.prisma.tes_categorias.findFirst({
      where: { id: categoriaId, is_sistema: true },
    });
    if (!categoria) {
      throw new NotFoundException(`Categoría del sistema ${categoriaId} no encontrada`);
    }

    if (!cuentaContableId) {
      // Eliminar mapeo si existe
      await this.prisma.tes_categoria_cuenta_contable.deleteMany({
        where: { empresa_id: empresaId, categoria_id: categoriaId },
      });
      return { cuenta_contable_id: null };
    }

    return this.prisma.tes_categoria_cuenta_contable.upsert({
      where: {
        empresa_id_categoria_id: { empresa_id: empresaId, categoria_id: categoriaId },
      },
      create: { empresa_id: empresaId, categoria_id: categoriaId, cuenta_contable_id: cuentaContableId },
      update: { cuenta_contable_id: cuentaContableId },
    });
  }

  async getCuentaContableEfectiva(categoriaId: string, empresaId: string): Promise<string | null> {
    const categoria = await this.prisma.tes_categorias.findUnique({
      where: { id: categoriaId },
    });
    if (!categoria) return null;

    if (categoria.is_sistema) {
      const mapeo = await this.prisma.tes_categoria_cuenta_contable.findUnique({
        where: { empresa_id_categoria_id: { empresa_id: empresaId, categoria_id: categoriaId } },
      });
      return mapeo?.cuenta_contable_id ?? null;
    }

    return categoria.cuenta_contable_id ?? null;
  }

  async create(dto: CreateTesCategoriaDto, empresaId: string) {
    return this.prisma.tes_categorias.create({
      data: {
        empresa_id: empresaId,
        nombre: dto.nombre,
        descripcion: dto.descripcion ?? null,
        tipo: dto.tipo,
        cuenta_contable_id: dto.cuenta_contable_id ?? null,
        requiere_comprobante: dto.requiere_comprobante ?? false,
        is_sistema: false,
        activo: true,
      },
    });
  }

  async update(id: string, dto: UpdateTesCategoriaDto, empresaId: string) {
    const categoria = await this.prisma.tes_categorias.findFirst({
      where: {
        id,
        OR: [{ empresa_id: empresaId }, { is_sistema: true }],
      },
    });
    if (!categoria) {
      throw new NotFoundException(`Categoría ${id} no encontrada`);
    }
    if (categoria.is_sistema) {
      throw new BadRequestException('No se pueden modificar categorías del sistema');
    }

    return this.prisma.tes_categorias.update({
      where: { id },
      data: {
        ...(dto.nombre !== undefined && { nombre: dto.nombre }),
        ...(dto.descripcion !== undefined && { descripcion: dto.descripcion ?? null }),
        ...(dto.tipo !== undefined && { tipo: dto.tipo }),
        ...(dto.cuenta_contable_id !== undefined && { cuenta_contable_id: dto.cuenta_contable_id }),
        ...(dto.requiere_comprobante !== undefined && { requiere_comprobante: dto.requiere_comprobante }),
      },
    });
  }

  async remove(id: string, empresaId: string) {
    const categoria = await this.prisma.tes_categorias.findFirst({
      where: {
        id,
        OR: [{ empresa_id: empresaId }, { is_sistema: true }],
      },
    });
    if (!categoria) {
      throw new NotFoundException(`Categoría ${id} no encontrada`);
    }
    if (categoria.is_sistema) {
      throw new BadRequestException('No se pueden eliminar categorías del sistema');
    }

    // Verificar que no tenga movimientos asociados
    const enUso = await this.prisma.tes_movimiento_det.findFirst({
      where: { categoria_id: id },
    });
    if (enUso) {
      throw new BadRequestException('No se puede eliminar una categoría que ya tiene movimientos registrados');
    }

    return this.prisma.tes_categorias.delete({ where: { id } });
  }
}
