/**
 * Product Service
 * Servicio para gestion de productos comerciales
 *
 * @module Products
 */

import { Repository, FindOptionsWhere, In } from 'typeorm';
import { AppDataSource } from '../../../shared/database/typeorm.config';
import { Product } from '../entities/product.entity';

export interface CreateProductDto {
  tenantId: string;
  categoryId?: string;
  inventoryProductId?: string;
  sku: string;
  barcode?: string;
  name: string;
  shortName?: string;
  description?: string;
  productType?: 'product' | 'service' | 'consumable' | 'kit';
  salePrice?: number;
  costPrice?: number;
  minSalePrice?: number;
  currency?: string;
  taxRate?: number;
  taxIncluded?: boolean;
  satProductCode?: string;
  satUnitCode?: string;
  uom?: string;
  uomPurchase?: string;
  conversionFactor?: number;
  trackInventory?: boolean;
  minStock?: number;
  maxStock?: number;
  reorderPoint?: number;
  reorderQuantity?: number;
  trackLots?: boolean;
  trackSerials?: boolean;
  trackExpiry?: boolean;
  weight?: number;
  weightUnit?: string;
  length?: number;
  width?: number;
  height?: number;
  dimensionUnit?: string;
  imageUrl?: string;
  images?: string[];
  tags?: string[];
  notes?: string;
  createdBy?: string;
}

export interface UpdateProductDto {
  categoryId?: string | null;
  inventoryProductId?: string | null;
  sku?: string;
  barcode?: string;
  name?: string;
  shortName?: string;
  description?: string;
  productType?: 'product' | 'service' | 'consumable' | 'kit';
  salePrice?: number;
  costPrice?: number;
  minSalePrice?: number;
  currency?: string;
  taxRate?: number;
  taxIncluded?: boolean;
  satProductCode?: string;
  satUnitCode?: string;
  uom?: string;
  uomPurchase?: string;
  conversionFactor?: number;
  trackInventory?: boolean;
  minStock?: number;
  maxStock?: number;
  reorderPoint?: number;
  reorderQuantity?: number;
  trackLots?: boolean;
  trackSerials?: boolean;
  trackExpiry?: boolean;
  weight?: number;
  weightUnit?: string;
  length?: number;
  width?: number;
  height?: number;
  dimensionUnit?: string;
  imageUrl?: string;
  images?: string[];
  tags?: string[];
  notes?: string;
  isActive?: boolean;
  isSellable?: boolean;
  isPurchasable?: boolean;
  updatedBy?: string;
}

export interface ProductFilters {
  tenantId: string;
  categoryId?: string;
  productType?: 'product' | 'service' | 'consumable' | 'kit';
  isActive?: boolean;
  isSellable?: boolean;
  isPurchasable?: boolean;
  search?: string;
  tags?: string[];
  limit?: number;
  offset?: number;
}

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

export interface PaginatedResult<T> {
  data: T[];
  total: number;
  limit: number;
  offset: number;
}

export class ProductService {
  private repository: Repository<Product>;

  constructor() {
    this.repository = AppDataSource.getRepository(Product);
  }

  async findAll(filters: ProductFilters): Promise<PaginatedResult<Product>> {
    const where: FindOptionsWhere<Product> = {
      tenantId: filters.tenantId,
    };

    if (filters.categoryId) {
      where.categoryId = filters.categoryId;
    }

    if (filters.productType) {
      where.productType = filters.productType;
    }

    if (filters.isActive !== undefined) {
      where.isActive = filters.isActive;
    }

    if (filters.isSellable !== undefined) {
      where.isSellable = filters.isSellable;
    }

    if (filters.isPurchasable !== undefined) {
      where.isPurchasable = filters.isPurchasable;
    }

    const queryBuilder = this.repository.createQueryBuilder('product')
      .where(where)
      .leftJoinAndSelect('product.category', 'category');

    if (filters.search) {
      queryBuilder.andWhere(
        '(product.name ILIKE :search OR product.sku ILIKE :search OR product.barcode ILIKE :search)',
        { search: `%${filters.search}%` }
      );
    }

    if (filters.tags && filters.tags.length > 0) {
      queryBuilder.andWhere('product.tags && :tags', { tags: filters.tags });
    }

    const total = await queryBuilder.getCount();

    queryBuilder
      .orderBy('product.name', 'ASC')
      .limit(filters.limit || 50)
      .offset(filters.offset || 0);

    const data = await queryBuilder.getMany();

    return {
      data,
      total,
      limit: filters.limit || 50,
      offset: filters.offset || 0,
    };
  }

  async findById(id: string, tenantId: string): Promise<Product | null> {
    return this.repository.findOne({
      where: { id, tenantId },
      relations: ['category'],
    });
  }

  async findBySku(sku: string, tenantId: string): Promise<Product | null> {
    return this.repository.findOne({
      where: { sku, tenantId },
      relations: ['category'],
    });
  }

  async findByBarcode(barcode: string, tenantId: string): Promise<Product | null> {
    return this.repository.findOne({
      where: { barcode, tenantId },
      relations: ['category'],
    });
  }

  async findByCategory(categoryId: string, tenantId: string): Promise<Product[]> {
    return this.repository.find({
      where: { categoryId, tenantId, isActive: true },
      order: { name: 'ASC' },
    });
  }

  async findByIds(ids: string[], tenantId: string): Promise<Product[]> {
    return this.repository.find({
      where: { id: In(ids), tenantId },
      relations: ['category'],
    });
  }

  async create(data: CreateProductDto): Promise<Product> {
    const product = this.repository.create(data);
    return this.repository.save(product);
  }

  async update(
    id: string,
    tenantId: string,
    data: UpdateProductDto
  ): Promise<Product | null> {
    const product = await this.findById(id, tenantId);
    if (!product) {
      return null;
    }

    Object.assign(product, data);
    return this.repository.save(product);
  }

  async delete(id: string, tenantId: string): Promise<boolean> {
    const result = await this.repository.softDelete({ id, tenantId });
    return result.affected ? result.affected > 0 : false;
  }

  async hardDelete(id: string, tenantId: string): Promise<boolean> {
    const result = await this.repository.delete({ id, tenantId });
    return result.affected ? result.affected > 0 : false;
  }

  async updatePrices(
    id: string,
    tenantId: string,
    prices: { salePrice?: number; costPrice?: number; minSalePrice?: number }
  ): Promise<Product | null> {
    const product = await this.findById(id, tenantId);
    if (!product) {
      return null;
    }

    if (prices.salePrice !== undefined) product.salePrice = prices.salePrice;
    if (prices.costPrice !== undefined) product.costPrice = prices.costPrice;
    if (prices.minSalePrice !== undefined) product.minSalePrice = prices.minSalePrice;

    return this.repository.save(product);
  }

  async updateStock(
    id: string,
    tenantId: string,
    stockData: { minStock?: number; maxStock?: number; reorderPoint?: number; reorderQuantity?: number }
  ): Promise<Product | null> {
    const product = await this.findById(id, tenantId);
    if (!product) {
      return null;
    }

    Object.assign(product, stockData);
    return this.repository.save(product);
  }

  async toggleActive(id: string, tenantId: string): Promise<Product | null> {
    const product = await this.findById(id, tenantId);
    if (!product) {
      return null;
    }

    product.isActive = !product.isActive;
    return this.repository.save(product);
  }

  async countByCategory(categoryId: string, tenantId: string): Promise<number> {
    return this.repository.count({
      where: { categoryId, tenantId },
    });
  }

  async searchProducts(
    tenantId: string,
    query: string,
    limit: number = 20
  ): Promise<Product[]> {
    return this.repository.createQueryBuilder('product')
      .where('product.tenantId = :tenantId', { tenantId })
      .andWhere('product.isActive = true')
      .andWhere(
        '(product.name ILIKE :query OR product.sku ILIKE :query OR product.barcode ILIKE :query OR product.shortName ILIKE :query)',
        { query: `%${query}%` }
      )
      .leftJoinAndSelect('product.category', 'category')
      .orderBy('product.name', 'ASC')
      .limit(limit)
      .getMany();
  }

  async getLowStockProducts(tenantId: string): Promise<Product[]> {
    return this.repository.createQueryBuilder('product')
      .where('product.tenantId = :tenantId', { tenantId })
      .andWhere('product.isActive = true')
      .andWhere('product.trackInventory = true')
      .andWhere('product.reorderPoint IS NOT NULL')
      .leftJoinAndSelect('product.category', 'category')
      .orderBy('product.name', 'ASC')
      .getMany();
  }

  async bulkUpdatePrices(
    tenantId: string,
    updates: Array<{ id: string; salePrice?: number; costPrice?: number }>
  ): Promise<number> {
    let updated = 0;

    for (const update of updates) {
      const result = await this.repository.update(
        { id: update.id, tenantId },
        {
          ...(update.salePrice !== undefined && { salePrice: update.salePrice }),
          ...(update.costPrice !== undefined && { costPrice: update.costPrice }),
        }
      );
      if (result.affected) {
        updated += result.affected;
      }
    }

    return updated;
  }
}
