/**
 * Product Attribute Service
 * Servicio para gestion de atributos de productos (color, talla, etc.)
 *
 * @module Products
 */

import { Repository, FindOptionsWhere } from 'typeorm';
import { AppDataSource } from '../../../shared/database/typeorm.config';
import { ProductAttribute } from '../entities/product-attribute.entity';
import { ProductAttributeValue } from '../entities/product-attribute-value.entity';

export interface CreateProductAttributeDto {
  tenantId: string;
  code: string;
  name: string;
  description?: string;
  displayType?: 'radio' | 'select' | 'color' | 'pills';
  sortOrder?: number;
  createdBy?: string;
}

export interface UpdateProductAttributeDto {
  code?: string;
  name?: string;
  description?: string;
  displayType?: 'radio' | 'select' | 'color' | 'pills';
  sortOrder?: number;
  isActive?: boolean;
  updatedBy?: string;
}

export interface CreateAttributeValueDto {
  attributeId: string;
  code?: string;
  name: string;
  htmlColor?: string;
  imageUrl?: string;
  sortOrder?: number;
}

export interface UpdateAttributeValueDto {
  code?: string;
  name?: string;
  htmlColor?: string;
  imageUrl?: string;
  sortOrder?: number;
  isActive?: boolean;
}

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

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

export class ProductAttributeService {
  private attributeRepository: Repository<ProductAttribute>;
  private valueRepository: Repository<ProductAttributeValue>;

  constructor() {
    this.attributeRepository = AppDataSource.getRepository(ProductAttribute);
    this.valueRepository = AppDataSource.getRepository(ProductAttributeValue);
  }

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

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

    const queryBuilder = this.attributeRepository.createQueryBuilder('attr')
      .where(where)
      .leftJoinAndSelect('attr.values', 'values', 'values.isActive = true')
      .orderBy('attr.sortOrder', 'ASC')
      .addOrderBy('attr.name', 'ASC')
      .addOrderBy('values.sortOrder', 'ASC');

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

    return queryBuilder.getMany();
  }

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

  async findByCode(code: string, tenantId: string): Promise<ProductAttribute | null> {
    return this.attributeRepository.findOne({
      where: { code, tenantId },
      relations: ['values'],
    });
  }

  async create(data: CreateProductAttributeDto): Promise<ProductAttribute> {
    const attribute = this.attributeRepository.create(data);
    return this.attributeRepository.save(attribute);
  }

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

    Object.assign(attribute, data);
    return this.attributeRepository.save(attribute);
  }

  async delete(id: string, tenantId: string): Promise<boolean> {
    const attribute = await this.findById(id, tenantId);
    if (!attribute) {
      return false;
    }

    await this.valueRepository.delete({ attributeId: id });

    const result = await this.attributeRepository.delete({ id, tenantId });
    return result.affected ? result.affected > 0 : false;
  }

  async findValueById(id: string): Promise<ProductAttributeValue | null> {
    return this.valueRepository.findOne({
      where: { id },
      relations: ['attribute'],
    });
  }

  async findValuesByAttribute(attributeId: string): Promise<ProductAttributeValue[]> {
    return this.valueRepository.find({
      where: { attributeId, isActive: true },
      order: { sortOrder: 'ASC', name: 'ASC' },
    });
  }

  async createValue(data: CreateAttributeValueDto): Promise<ProductAttributeValue> {
    const value = this.valueRepository.create(data);
    return this.valueRepository.save(value);
  }

  async updateValue(
    id: string,
    data: UpdateAttributeValueDto
  ): Promise<ProductAttributeValue | null> {
    const value = await this.findValueById(id);
    if (!value) {
      return null;
    }

    Object.assign(value, data);
    return this.valueRepository.save(value);
  }

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

  async addValues(
    attributeId: string,
    values: Array<{ code?: string; name: string; htmlColor?: string; sortOrder?: number }>
  ): Promise<ProductAttributeValue[]> {
    const created: ProductAttributeValue[] = [];

    for (let i = 0; i < values.length; i++) {
      const val = values[i];
      const value = this.valueRepository.create({
        attributeId,
        code: val.code,
        name: val.name,
        htmlColor: val.htmlColor,
        sortOrder: val.sortOrder ?? i,
      });
      const saved = await this.valueRepository.save(value);
      created.push(saved);
    }

    return created;
  }

  async reorderValues(
    attributeId: string,
    valueIds: string[]
  ): Promise<void> {
    for (let i = 0; i < valueIds.length; i++) {
      await this.valueRepository.update(
        { id: valueIds[i], attributeId },
        { sortOrder: i }
      );
    }
  }

  async countValues(attributeId: string): Promise<number> {
    return this.valueRepository.count({
      where: { attributeId, isActive: true },
    });
  }

  async createWithValues(
    data: CreateProductAttributeDto,
    values: Array<{ code?: string; name: string; htmlColor?: string }>
  ): Promise<ProductAttribute> {
    const attribute = await this.create(data);

    if (values.length > 0) {
      await this.addValues(attribute.id, values);
      const refreshed = await this.findById(attribute.id, data.tenantId);
      if (refreshed) {
        return refreshed;
      }
    }

    return attribute;
  }
}
