/**
 * ConsumoObraService - Servicio de consumos de materiales
 *
 * Gestión de consumos de materiales por obra/lote.
 *
 * @module Inventory
 */

import { Repository, FindOptionsWhere } from 'typeorm';
import { ConsumoObra } from '../entities/consumo-obra.entity';

interface ServiceContext {
  tenantId: string;
  userId?: string;
}

interface PaginatedResult<T> {
  data: T[];
  total: number;
  page: number;
  limit: number;
  totalPages: number;
}

export interface CreateConsumoDto {
  fraccionamientoId: string;
  loteId?: string;
  departamentoId?: string;
  conceptoId?: string;
  productId: string;
  quantity: number;
  unitCost?: number;
  consumptionDate: Date;
  notes?: string;
}

export interface ConsumoFilters {
  fraccionamientoId?: string;
  loteId?: string;
  conceptoId?: string;
  productId?: string;
  dateFrom?: Date;
  dateTo?: Date;
}

export interface ConsumoStats {
  totalConsumos: number;
  totalQuantity: number;
  totalCost: number;
  byProduct: { productId: string; quantity: number; cost: number }[];
  byConcepto: { conceptoId: string; quantity: number; cost: number }[];
}

export class ConsumoObraService {
  constructor(private readonly repository: Repository<ConsumoObra>) {}

  async findWithFilters(
    ctx: ServiceContext,
    filters: ConsumoFilters = {},
    page: number = 1,
    limit: number = 20
  ): Promise<PaginatedResult<ConsumoObra>> {
    const skip = (page - 1) * limit;

    const queryBuilder = this.repository
      .createQueryBuilder('consumo')
      .leftJoinAndSelect('consumo.fraccionamiento', 'fraccionamiento')
      .leftJoinAndSelect('consumo.lote', 'lote')
      .leftJoinAndSelect('consumo.concepto', 'concepto')
      .leftJoinAndSelect('consumo.registeredBy', 'registeredBy')
      .where('consumo.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('consumo.deleted_at IS NULL');

    if (filters.fraccionamientoId) {
      queryBuilder.andWhere('consumo.fraccionamiento_id = :fraccionamientoId', {
        fraccionamientoId: filters.fraccionamientoId,
      });
    }

    if (filters.loteId) {
      queryBuilder.andWhere('consumo.lote_id = :loteId', { loteId: filters.loteId });
    }

    if (filters.conceptoId) {
      queryBuilder.andWhere('consumo.concepto_id = :conceptoId', { conceptoId: filters.conceptoId });
    }

    if (filters.productId) {
      queryBuilder.andWhere('consumo.product_id = :productId', { productId: filters.productId });
    }

    if (filters.dateFrom) {
      queryBuilder.andWhere('consumo.consumption_date >= :dateFrom', { dateFrom: filters.dateFrom });
    }

    if (filters.dateTo) {
      queryBuilder.andWhere('consumo.consumption_date <= :dateTo', { dateTo: filters.dateTo });
    }

    queryBuilder
      .orderBy('consumo.consumption_date', 'DESC')
      .skip(skip)
      .take(limit);

    const [data, total] = await queryBuilder.getManyAndCount();

    return {
      data,
      total,
      page,
      limit,
      totalPages: Math.ceil(total / limit),
    };
  }

  async findById(ctx: ServiceContext, id: string): Promise<ConsumoObra | null> {
    return this.repository.findOne({
      where: {
        id,
        tenantId: ctx.tenantId,
        deletedAt: null,
      } as unknown as FindOptionsWhere<ConsumoObra>,
      relations: ['fraccionamiento', 'lote', 'concepto', 'registeredBy'],
    });
  }

  async create(ctx: ServiceContext, dto: CreateConsumoDto): Promise<ConsumoObra> {
    const consumo = this.repository.create({
      tenantId: ctx.tenantId,
      createdById: ctx.userId,
      registeredById: ctx.userId,
      fraccionamientoId: dto.fraccionamientoId,
      loteId: dto.loteId,
      departamentoId: dto.departamentoId,
      conceptoId: dto.conceptoId,
      productId: dto.productId,
      quantity: dto.quantity,
      unitCost: dto.unitCost,
      consumptionDate: dto.consumptionDate,
      notes: dto.notes,
    });

    return this.repository.save(consumo);
  }

  async getStats(ctx: ServiceContext, fraccionamientoId: string): Promise<ConsumoStats> {
    const baseQuery = this.repository
      .createQueryBuilder('consumo')
      .where('consumo.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('consumo.fraccionamiento_id = :fraccionamientoId', { fraccionamientoId })
      .andWhere('consumo.deleted_at IS NULL');

    // Get totals
    const totals = await baseQuery
      .clone()
      .select('COUNT(*)', 'total')
      .addSelect('SUM(consumo.quantity)', 'totalQuantity')
      .addSelect('SUM(consumo.quantity * COALESCE(consumo.unit_cost, 0))', 'totalCost')
      .getRawOne();

    // Get by product
    const byProduct = await baseQuery
      .clone()
      .select('consumo.product_id', 'productId')
      .addSelect('SUM(consumo.quantity)', 'quantity')
      .addSelect('SUM(consumo.quantity * COALESCE(consumo.unit_cost, 0))', 'cost')
      .groupBy('consumo.product_id')
      .getRawMany();

    // Get by concepto
    const byConcepto = await baseQuery
      .clone()
      .select('consumo.concepto_id', 'conceptoId')
      .addSelect('SUM(consumo.quantity)', 'quantity')
      .addSelect('SUM(consumo.quantity * COALESCE(consumo.unit_cost, 0))', 'cost')
      .where('consumo.concepto_id IS NOT NULL')
      .groupBy('consumo.concepto_id')
      .getRawMany();

    return {
      totalConsumos: parseInt(totals.total || '0', 10),
      totalQuantity: parseFloat(totals.totalQuantity || '0'),
      totalCost: parseFloat(totals.totalCost || '0'),
      byProduct: byProduct.map((p) => ({
        productId: p.productId,
        quantity: parseFloat(p.quantity || '0'),
        cost: parseFloat(p.cost || '0'),
      })),
      byConcepto: byConcepto.map((c) => ({
        conceptoId: c.conceptoId,
        quantity: parseFloat(c.quantity || '0'),
        cost: parseFloat(c.cost || '0'),
      })),
    };
  }

  async softDelete(ctx: ServiceContext, id: string): Promise<boolean> {
    const consumo = await this.findById(ctx, id);
    if (!consumo) {
      return false;
    }

    await this.repository.update(
      { id, tenantId: ctx.tenantId } as unknown as FindOptionsWhere<ConsumoObra>,
      { deletedAt: new Date(), deletedById: ctx.userId }
    );

    return true;
  }
}
