/**
 * Part Service
 * Mecánicas Diesel - ERP Suite
 *
 * Business logic for parts/inventory management.
 */

import { Repository, DataSource } from 'typeorm';
import { Part } from '../entities/part.entity';

// DTOs
export interface CreatePartDto {
  sku: string;
  name: string;
  description?: string;
  categoryId?: string;
  preferredSupplierId?: string;
  brand?: string;
  manufacturer?: string;
  compatibleEngines?: string[];
  unit?: string;
  cost?: number;
  price: number;
  minStock?: number;
  maxStock?: number;
  reorderPoint?: number;
  locationId?: string;
  barcode?: string;
  notes?: string;
}

export interface UpdatePartDto {
  name?: string;
  description?: string;
  categoryId?: string;
  preferredSupplierId?: string;
  brand?: string;
  manufacturer?: string;
  compatibleEngines?: string[];
  unit?: string;
  cost?: number;
  price?: number;
  minStock?: number;
  maxStock?: number;
  reorderPoint?: number;
  locationId?: string;
  barcode?: string;
  isActive?: boolean;
}

export interface PartFilters {
  categoryId?: string;
  preferredSupplierId?: string;
  brand?: string;
  search?: string;
  lowStock?: boolean;
  isActive?: boolean;
}

export interface StockAdjustmentDto {
  quantity: number;
  reason: string;
  reference?: string;
}

export class PartService {
  private partRepository: Repository<Part>;

  constructor(dataSource: DataSource) {
    this.partRepository = dataSource.getRepository(Part);
  }

  /**
   * Create a new part
   */
  async create(tenantId: string, dto: CreatePartDto): Promise<Part> {
    // Check SKU uniqueness
    const existing = await this.partRepository.findOne({
      where: { tenantId, sku: dto.sku },
    });

    if (existing) {
      throw new Error(`Part with SKU ${dto.sku} already exists`);
    }

    const part = this.partRepository.create({
      tenantId,
      sku: dto.sku,
      name: dto.name,
      description: dto.description,
      categoryId: dto.categoryId,
      preferredSupplierId: dto.preferredSupplierId,
      brand: dto.brand,
      manufacturer: dto.manufacturer,
      compatibleEngines: dto.compatibleEngines,
      unit: dto.unit || 'pza',
      cost: dto.cost,
      price: dto.price,
      minStock: dto.minStock || 0,
      maxStock: dto.maxStock,
      reorderPoint: dto.reorderPoint,
      locationId: dto.locationId,
      barcode: dto.barcode,
      currentStock: 0,
      reservedStock: 0,
      isActive: true,
    });

    return this.partRepository.save(part);
  }

  /**
   * Find part by ID
   */
  async findById(tenantId: string, id: string): Promise<Part | null> {
    return this.partRepository.findOne({
      where: { id, tenantId },
    });
  }

  /**
   * Find part by SKU
   */
  async findBySku(tenantId: string, sku: string): Promise<Part | null> {
    return this.partRepository.findOne({
      where: { tenantId, sku },
    });
  }

  /**
   * Find part by barcode
   */
  async findByBarcode(tenantId: string, barcode: string): Promise<Part | null> {
    return this.partRepository.findOne({
      where: { tenantId, barcode },
    });
  }

  /**
   * List parts with filters
   */
  async findAll(
    tenantId: string,
    filters: PartFilters = {},
    pagination = { page: 1, limit: 20 }
  ) {
    const queryBuilder = this.partRepository.createQueryBuilder('part')
      .where('part.tenant_id = :tenantId', { tenantId });

    if (filters.categoryId) {
      queryBuilder.andWhere('part.category_id = :categoryId', { categoryId: filters.categoryId });
    }
    if (filters.preferredSupplierId) {
      queryBuilder.andWhere('part.preferred_supplier_id = :supplierId', { supplierId: filters.preferredSupplierId });
    }
    if (filters.brand) {
      queryBuilder.andWhere('part.brand = :brand', { brand: filters.brand });
    }
    if (filters.isActive !== undefined) {
      queryBuilder.andWhere('part.is_active = :isActive', { isActive: filters.isActive });
    }
    if (filters.lowStock) {
      queryBuilder.andWhere('part.current_stock <= part.min_stock');
    }
    if (filters.search) {
      queryBuilder.andWhere(
        '(part.sku ILIKE :search OR part.name ILIKE :search OR part.barcode ILIKE :search OR part.description ILIKE :search)',
        { search: `%${filters.search}%` }
      );
    }

    const skip = (pagination.page - 1) * pagination.limit;

    const [data, total] = await queryBuilder
      .orderBy('part.name', 'ASC')
      .skip(skip)
      .take(pagination.limit)
      .getManyAndCount();

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

  /**
   * Update part
   */
  async update(tenantId: string, id: string, dto: UpdatePartDto): Promise<Part | null> {
    const part = await this.findById(tenantId, id);
    if (!part) return null;

    Object.assign(part, dto);
    return this.partRepository.save(part);
  }

  /**
   * Adjust stock (increase or decrease)
   */
  async adjustStock(
    tenantId: string,
    id: string,
    dto: StockAdjustmentDto
  ): Promise<Part | null> {
    const part = await this.findById(tenantId, id);
    if (!part) return null;

    const newStock = part.currentStock + dto.quantity;

    if (newStock < 0) {
      throw new Error('Stock cannot be negative');
    }

    part.currentStock = newStock;

    // TODO: Create stock movement record for audit trail

    return this.partRepository.save(part);
  }

  /**
   * Reserve stock for an order
   */
  async reserveStock(tenantId: string, id: string, quantity: number): Promise<boolean> {
    const part = await this.findById(tenantId, id);
    if (!part) return false;

    const availableStock = part.currentStock - part.reservedStock;

    if (quantity > availableStock) {
      throw new Error(`Insufficient stock. Available: ${availableStock}, Requested: ${quantity}`);
    }

    part.reservedStock += quantity;
    await this.partRepository.save(part);
    return true;
  }

  /**
   * Release reserved stock
   */
  async releaseStock(tenantId: string, id: string, quantity: number): Promise<boolean> {
    const part = await this.findById(tenantId, id);
    if (!part) return false;

    part.reservedStock = Math.max(0, part.reservedStock - quantity);
    await this.partRepository.save(part);
    return true;
  }

  /**
   * Consume reserved stock (when order is completed)
   */
  async consumeStock(tenantId: string, id: string, quantity: number): Promise<boolean> {
    const part = await this.findById(tenantId, id);
    if (!part) return false;

    part.reservedStock = Math.max(0, part.reservedStock - quantity);
    part.currentStock = Math.max(0, part.currentStock - quantity);
    await this.partRepository.save(part);
    return true;
  }

  /**
   * Get parts with low stock
   */
  async getLowStockParts(tenantId: string): Promise<Part[]> {
    return this.partRepository
      .createQueryBuilder('part')
      .where('part.tenant_id = :tenantId', { tenantId })
      .andWhere('part.is_active = true')
      .andWhere('part.current_stock <= part.min_stock')
      .orderBy('part.current_stock', 'ASC')
      .getMany();
  }

  /**
   * Get inventory value
   */
  async getInventoryValue(tenantId: string): Promise<{
    totalCostValue: number;
    totalSaleValue: number;
    totalItems: number;
    lowStockCount: number;
  }> {
    const result = await this.partRepository
      .createQueryBuilder('part')
      .select('SUM(part.current_stock * COALESCE(part.cost, 0))', 'costValue')
      .addSelect('SUM(part.current_stock * part.price)', 'saleValue')
      .addSelect('SUM(part.current_stock)', 'totalItems')
      .where('part.tenant_id = :tenantId', { tenantId })
      .andWhere('part.is_active = true')
      .getRawOne();

    const lowStockCount = await this.partRepository
      .createQueryBuilder('part')
      .where('part.tenant_id = :tenantId', { tenantId })
      .andWhere('part.is_active = true')
      .andWhere('part.current_stock <= part.min_stock')
      .getCount();

    return {
      totalCostValue: parseFloat(result?.costValue) || 0,
      totalSaleValue: parseFloat(result?.saleValue) || 0,
      totalItems: parseInt(result?.totalItems, 10) || 0,
      lowStockCount,
    };
  }

  /**
   * Search parts for autocomplete
   */
  async search(tenantId: string, query: string, limit = 10): Promise<Part[]> {
    return this.partRepository
      .createQueryBuilder('part')
      .where('part.tenant_id = :tenantId', { tenantId })
      .andWhere('part.is_active = true')
      .andWhere(
        '(part.sku ILIKE :query OR part.name ILIKE :query OR part.barcode ILIKE :query)',
        { query: `%${query}%` }
      )
      .orderBy('part.name', 'ASC')
      .take(limit)
      .getMany();
  }

  /**
   * Deactivate part
   */
  async deactivate(tenantId: string, id: string): Promise<boolean> {
    const part = await this.findById(tenantId, id);
    if (!part) return false;

    part.isActive = false;
    await this.partRepository.save(part);
    return true;
  }
}
