import { BadRequestException, Injectable, Logger, NotFoundException } from '@nestjs/common';
import { AuditService } from 'src/audit/audit.service';
import { PrismaService } from '../prisma/prisma.service';
import { CreateProveedorMarcaDto } from './dto/create-proveedor-marca.dto';
import { UpdateProveedorMarcaDto } from './dto/update-proveedor-marca.dto';

@Injectable()
export class ProveedorMarcaService {
  private readonly logger = new Logger(ProveedorMarcaService.name);

  constructor(
    private readonly prisma: PrismaService,
    private readonly auditService: AuditService,
  ) {}

  async findAll(
    empresa_id: string,
    filtros: { proveedor_id?: string; marca_id?: string; activo?: boolean },
  ) {
    const where: any = { empresa_id };
    if (filtros.proveedor_id) where.proveedor_id = filtros.proveedor_id;
    if (filtros.marca_id) where.marca_id = filtros.marca_id;
    if (typeof filtros.activo === 'boolean') where.activo = filtros.activo;

    return this.prisma.proveedor_marca.findMany({
      where,
      include: {
        marca: { select: { id: true, codigo: true, descripcion: true, active: true } },
        proveedor: {
          select: {
            id: true,
            personas: { select: { razon_social: true, ruc: true } },
          },
        },
      },
      orderBy: [{ activo: 'desc' }, { vigente_desde: 'desc' }],
    });
  }

  async findOne(id: string, empresa_id: string) {
    const row = await this.prisma.proveedor_marca.findFirst({
      where: { id, empresa_id },
      include: {
        marca: true,
        proveedor: { include: { personas: true } },
      },
    });
    if (!row) throw new NotFoundException('Vínculo proveedor-marca no encontrado');
    return row;
  }

  async findByProveedor(proveedor_id: string, empresa_id: string) {
    return this.prisma.proveedor_marca.findMany({
      where: { empresa_id, proveedor_id },
      include: { marca: { select: { id: true, codigo: true, descripcion: true, active: true } } },
      orderBy: [{ activo: 'desc' }, { marca: { descripcion: 'asc' } }],
    });
  }

  async findByMarca(marca_id: string, empresa_id: string) {
    return this.prisma.proveedor_marca.findMany({
      where: { empresa_id, marca_id },
      include: {
        proveedor: {
          select: {
            id: true,
            personas: { select: { razon_social: true, ruc: true } },
          },
        },
      },
      orderBy: [{ activo: 'desc' }, { es_proveedor_preferente: 'desc' }],
    });
  }

  async create(dto: CreateProveedorMarcaDto, empresa_id: string, user_id: string) {
    // Validar proveedor y marca pertenecen a la empresa
    const [proveedor, marca] = await Promise.all([
      this.prisma.proveedores.findFirst({ where: { id: dto.proveedor_id, empresa_id } }),
      this.prisma.marcas.findFirst({ where: { id: dto.marca_id, empresa_id } }),
    ]);
    if (!proveedor) throw new BadRequestException('Proveedor no encontrado en la empresa');
    if (!marca) throw new BadRequestException('Marca no encontrada en la empresa');

    const vigente_desde = dto.vigente_desde ?? new Date();

    // Si se marca preferente, desactivar el preferente vigente actual de la marca
    if (dto.es_proveedor_preferente) {
      await this.prisma.proveedor_marca.updateMany({
        where: {
          empresa_id,
          marca_id: dto.marca_id,
          es_proveedor_preferente: true,
          activo: true,
          NOT: { proveedor_id: dto.proveedor_id },
        },
        data: { es_proveedor_preferente: false, updated_at: new Date() },
      });
    }

    // Si ya existe un vínculo para (proveedor, marca), reactivar/actualizar en vez de fallar.
    // Esto cubre el caso de "desvinculé por accidente" y resuelve el choque con el índice único.
    const existente = await this.prisma.proveedor_marca.findFirst({
      where: { empresa_id, proveedor_id: dto.proveedor_id, marca_id: dto.marca_id },
      orderBy: [{ activo: 'desc' }, { vigente_desde: 'desc' }],
    });

    if (existente) {
      if (existente.activo) {
        throw new BadRequestException(
          'Este proveedor ya tiene esta marca vinculada y activa',
        );
      }
      const row = await this.prisma.proveedor_marca.update({
        where: { id: existente.id },
        data: {
          activo: true,
          es_distribuidor_oficial: dto.es_distribuidor_oficial ?? false,
          es_proveedor_preferente: dto.es_proveedor_preferente ?? false,
          vigente_desde,
          vigente_hasta: dto.vigente_hasta ?? null,
          observacion: dto.observacion ?? existente.observacion,
          inferido: false,
          usuario_id: user_id,
        },
      });
      await this.auditService.log({
        empresa_id,
        user_id,
        action: 'UPDATE',
        entity_type: 'proveedor_marca',
        entity_id: row.id,
        descripcion: `Vínculo proveedor-marca reactivado (proveedor=${dto.proveedor_id}, marca=${marca.descripcion})`,
        old_value: existente as any,
        new_value: row as any,
      });
      return { message: 'Vínculo reactivado', data: row, reactivado: true };
    }

    try {
      const row = await this.prisma.proveedor_marca.create({
        data: {
          empresa_id,
          proveedor_id: dto.proveedor_id,
          marca_id: dto.marca_id,
          es_distribuidor_oficial: dto.es_distribuidor_oficial ?? false,
          es_proveedor_preferente: dto.es_proveedor_preferente ?? false,
          vigente_desde,
          vigente_hasta: dto.vigente_hasta ?? null,
          observacion: dto.observacion ?? null,
          usuario_id: user_id,
        },
      });

      await this.auditService.log({
        empresa_id,
        user_id,
        action: 'CREATE',
        entity_type: 'proveedor_marca',
        entity_id: row.id,
        descripcion: `Vínculo proveedor-marca creado (proveedor=${dto.proveedor_id}, marca=${marca.descripcion})`,
        new_value: row as any,
      });
      return { message: 'Vínculo creado', data: row };
    } catch (e: any) {
      if (e?.code === 'P2002') {
        throw new BadRequestException(
          'Ya existe un vínculo proveedor-marca para esa misma fecha de vigencia',
        );
      }
      throw e;
    }
  }

  async update(id: string, dto: UpdateProveedorMarcaDto, empresa_id: string, user_id: string) {
    const existing = await this.prisma.proveedor_marca.findFirst({ where: { id, empresa_id } });
    if (!existing) throw new NotFoundException('Vínculo no encontrado');

    if (dto.es_proveedor_preferente === true) {
      await this.prisma.proveedor_marca.updateMany({
        where: {
          empresa_id,
          marca_id: existing.marca_id,
          es_proveedor_preferente: true,
          activo: true,
          NOT: { id },
        },
        data: { es_proveedor_preferente: false, updated_at: new Date() },
      });
    }

    const row = await this.prisma.proveedor_marca.update({
      where: { id },
      data: {
        es_distribuidor_oficial: dto.es_distribuidor_oficial ?? undefined,
        es_proveedor_preferente: dto.es_proveedor_preferente ?? undefined,
        vigente_desde: dto.vigente_desde ?? undefined,
        vigente_hasta: dto.vigente_hasta ?? undefined,
        observacion: dto.observacion ?? undefined,
        activo: dto.activo ?? undefined,
      },
    });

    await this.auditService.log({
      empresa_id,
      user_id,
      action: 'UPDATE',
      entity_type: 'proveedor_marca',
      entity_id: id,
      descripcion: `Vínculo proveedor-marca actualizado`,
      old_value: existing as any,
      new_value: row as any,
    });
    return { message: 'Vínculo actualizado', data: row };
  }

  async remove(id: string, empresa_id: string, user_id: string) {
    const existing = await this.prisma.proveedor_marca.findFirst({ where: { id, empresa_id } });
    if (!existing) throw new NotFoundException('Vínculo no encontrado');

    // Soft-delete: desactivar
    const row = await this.prisma.proveedor_marca.update({
      where: { id },
      data: { activo: false, es_proveedor_preferente: false },
    });
    await this.auditService.log({
      empresa_id,
      user_id,
      action: 'DELETE',
      entity_type: 'proveedor_marca',
      entity_id: id,
      descripcion: `Vínculo proveedor-marca desactivado`,
      old_value: existing as any,
    });
    return { message: 'Vínculo desactivado', data: row };
  }

  /**
   * Backfill: infiere proveedor_marca a partir del histórico de compras.
   * Para cada par (compra_cab.proveedor_id, producto.marca_id) sin vínculo,
   * crea uno marcado como inferido = true.
   */
  async backfill(empresa_id: string, user_id: string) {
    const rows: Array<{ proveedor_id: string; marca_id: string; primera_compra: Date }> =
      await this.prisma.$queryRaw`
        SELECT cc.proveedor_id::text AS proveedor_id,
               p.marca_id::text AS marca_id,
               MIN(cc.fecha_emision) AS primera_compra
          FROM compra_cab cc
          JOIN compra_det cd ON cd.compra_cab_id = cc.id
          JOIN productos p   ON p.id = cd.producto_id
         WHERE cc.empresa_id = ${empresa_id}::uuid
           AND cc.proveedor_id IS NOT NULL
           AND p.marca_id IS NOT NULL
         GROUP BY cc.proveedor_id, p.marca_id
      `;

    let creados = 0;
    let omitidos = 0;
    for (const r of rows) {
      const exists = await this.prisma.proveedor_marca.findFirst({
        where: { empresa_id, proveedor_id: r.proveedor_id, marca_id: r.marca_id },
        select: { id: true },
      });
      if (exists) {
        omitidos++;
        continue;
      }
      await this.prisma.proveedor_marca.create({
        data: {
          empresa_id,
          proveedor_id: r.proveedor_id,
          marca_id: r.marca_id,
          vigente_desde: r.primera_compra ?? new Date(),
          inferido: true,
          activo: true,
          usuario_id: user_id,
          observacion: 'Inferido desde histórico de compras (backfill)',
        },
      });
      creados++;
    }

    await this.auditService.log({
      empresa_id,
      user_id,
      action: 'CREATE',
      entity_type: 'proveedor_marca',
      entity_id: 'BACKFILL',
      descripcion: `Backfill proveedor-marca: ${creados} creados, ${omitidos} ya existentes`,
      new_value: { creados, omitidos, total: rows.length },
    });
    this.logger.log(
      `Backfill proveedor-marca empresa=${empresa_id} creados=${creados} omitidos=${omitidos}`,
    );
    return { message: 'Backfill completado', creados, omitidos, total: rows.length };
  }
}
