import { Injectable } from '@nestjs/common';
import { formatDateTimeResponse } from 'src/utils/utilidades';
import { PrismaService } from '../prisma/prisma.service';
import { alcanceSucursalUsuario, filtroSucursalDeposito } from 'src/utils/alcance-sucursal';

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

  async findByProducto(producto_id: string, empresa_id: string) {
    // Validar que producto_id sea un UUID válido
    const uuidRegex =
      /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
    if (!uuidRegex.test(producto_id)) {
      return []; // Retornar array vacío si no es UUID válido
    }

    return this.prisma.movimientos_inventario.findMany({
      where: {
        producto_id,
        deposito_origen: { empresa_id },
      },
      include: {
        deposito_origen: { include: { sucursal: true } },
        deposito_destino: { include: { sucursal: true } },
        tipo_movimiento: true,
        producto: true,
      },
      orderBy: { created_at: 'desc' },
    });
  }

  async findByDeposito(deposito_id: string, empresa_id: string) {
    return this.prisma.movimientos_inventario.findMany({
      where: {
        OR: [
          { deposito_origen_id: deposito_id },
          { deposito_destino_id: deposito_id },
        ],
        // Sin esto, el id del depósito en la URL alcanzaba para ver los
        // movimientos de inventario de otra empresa.
        producto: { empresa_id },
      },
      include: {
        deposito_origen: { include: { sucursal: true } },
        deposito_destino: { include: { sucursal: true } },
        tipo_movimiento: true,
        producto: true,
      },
      orderBy: { created_at: 'desc' },
    });
  }

  async findAll(
    empresa_id: string,
    params?: {
      page?: number;
      limit?: number;
      producto_id?: string;
      deposito_id?: string;
      sucursal_id?: string;
      tipo_movimiento_id?: string;
      documento_origen?: string;
      search?: string;
      fecha_desde?: Date;
      fecha_hasta?: Date;
      /** Quién consulta. Define qué sucursales puede ver. */
      usuarioId?: string;
    },
  ) {
    const take = Math.max(1, Math.min(100, Number(params?.limit) || 25));
    const currentPage = Math.max(1, Number(params?.page) || 1);
    const skip = (currentPage - 1) * take;

    const where: any = {
      deposito_origen: { empresa_id },
    };

    if (params?.producto_id) {
      where.producto_id = params.producto_id;
    }
    const andClauses: any[] = [];
    if (params?.deposito_id) {
      andClauses.push({
        OR: [
          { deposito_origen_id: params.deposito_id },
          { deposito_destino_id: params.deposito_id },
        ],
      });
    }
    // El alcance del usuario manda sobre la sucursal pedida: si pide una que no
    // tiene asignada, la lista sale vacía. El movimiento se ata a la sucursal
    // por su depósito de origen.
    const alcance = await alcanceSucursalUsuario(this.prisma, params?.usuarioId, empresa_id);
    const filtroSucursal = filtroSucursalDeposito(alcance, params?.sucursal_id);
    if (filtroSucursal !== undefined) {
      where.deposito_origen = { ...where.deposito_origen, sucursal_id: filtroSucursal };
    }
    if (params?.tipo_movimiento_id) {
      where.tipo_movimiento_id = params.tipo_movimiento_id;
    }
    if (params?.documento_origen) {
      where.documento_origen = { contains: params.documento_origen, mode: 'insensitive' };
    }
    if (params?.search) {
      const term = params.search.trim();
      if (term) {
        andClauses.push({
          OR: [
            { documento_origen: { contains: term, mode: 'insensitive' } },
            { observaciones: { contains: term, mode: 'insensitive' } },
          ],
        });
      }
    }
    if (andClauses.length) {
      where.AND = andClauses;
    }
    if (params?.fecha_desde || params?.fecha_hasta) {
      where.created_at = {};
      if (params?.fecha_desde) where.created_at.gte = params.fecha_desde;
      if (params?.fecha_hasta) where.created_at.lte = params.fecha_hasta;
    }

    const [movimientos, total] = await this.prisma.$transaction([
      this.prisma.movimientos_inventario.findMany({
        where,
        include: {
          deposito_origen: { include: { sucursal: true } },
          deposito_destino: { include: { sucursal: true } },
          tipo_movimiento: true,
          producto: true,
        },
        orderBy: { created_at: 'desc' },
        skip,
        take,
      }),
      this.prisma.movimientos_inventario.count({ where }),
    ]);

    const data = movimientos.map((mov) => ({
      ...mov,
      created_at: formatDateTimeResponse(mov.created_at),
    }));

    return {
      data,
      total,
      page: currentPage,
      limit: take,
      lastPage: Math.max(1, Math.ceil(total / take)),
    };
  }
}
