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

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

export interface CreateProductVariantDto {
  productId: string;
  tenantId: string;
  sku: string;
  barcode?: string;
  name: string;
  priceExtra?: number;
  costExtra?: number;
  stockQty?: number;
  imageUrl?: string;
  createdBy?: string;
}

export interface UpdateProductVariantDto {
  sku?: string;
  barcode?: string;
  name?: string;
  priceExtra?: number;
  costExtra?: number;
  stockQty?: number;
  imageUrl?: string;
  isActive?: boolean;
  updatedBy?: string;
}

export interface ProductVariantFilters {
  tenantId: string;
  productId?: string;
  isActive?: boolean;
  search?: string;
}

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

export class ProductVariantService {
  private repository: Repository<ProductVariant>;

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

  async findAll(filters: ProductVariantFilters): Promise<ProductVariant[]> {
    const where: FindOptionsWhere<ProductVariant> = {
      tenantId: filters.tenantId,
    };

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

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

    const queryBuilder = this.repository.createQueryBuilder('variant')
      .where(where)
      .leftJoinAndSelect('variant.product', 'product')
      .orderBy('variant.name', 'ASC');

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

    return queryBuilder.getMany();
  }

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

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

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

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

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

  async create(data: CreateProductVariantDto): Promise<ProductVariant> {
    const variant = this.repository.create(data);
    return this.repository.save(variant);
  }

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

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

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

  async updateStock(
    id: string,
    tenantId: string,
    quantity: number,
    operation: 'set' | 'add' | 'subtract' = 'set'
  ): Promise<ProductVariant | null> {
    const variant = await this.findById(id, tenantId);
    if (!variant) {
      return null;
    }

    switch (operation) {
      case 'add':
        variant.stockQty = Number(variant.stockQty) + quantity;
        break;
      case 'subtract':
        variant.stockQty = Math.max(0, Number(variant.stockQty) - quantity);
        break;
      case 'set':
      default:
        variant.stockQty = quantity;
        break;
    }

    return this.repository.save(variant);
  }

  async updatePriceExtra(
    id: string,
    tenantId: string,
    priceExtra: number
  ): Promise<ProductVariant | null> {
    const variant = await this.findById(id, tenantId);
    if (!variant) {
      return null;
    }

    variant.priceExtra = priceExtra;
    return this.repository.save(variant);
  }

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

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

  async countByProduct(productId: string, tenantId: string): Promise<number> {
    return this.repository.count({
      where: { productId, tenantId, isActive: true },
    });
  }

  async getTotalStock(productId: string, tenantId: string): Promise<number> {
    const result = await this.repository.createQueryBuilder('variant')
      .select('SUM(variant.stockQty)', 'totalStock')
      .where('variant.productId = :productId', { productId })
      .andWhere('variant.tenantId = :tenantId', { tenantId })
      .andWhere('variant.isActive = true')
      .getRawOne();

    return result?.totalStock ? parseFloat(result.totalStock) : 0;
  }

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

  async bulkCreate(variants: CreateProductVariantDto[]): Promise<ProductVariant[]> {
    const entities = this.repository.create(variants);
    return this.repository.save(entities);
  }

  async deleteByProduct(productId: string, tenantId: string): Promise<number> {
    const result = await this.repository.delete({ productId, tenantId });
    return result.affected || 0;
  }
}
