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

import { Repository, FindOptionsWhere, IsNull } from 'typeorm';
import { AppDataSource } from '../../../shared/database/typeorm.config';
import { ProductCategory } from '../entities/product-category.entity';

export interface CreateProductCategoryDto {
  tenantId: string;
  parentId?: string;
  code: string;
  name: string;
  description?: string;
  imageUrl?: string;
  sortOrder?: number;
}

export interface UpdateProductCategoryDto {
  parentId?: string | null;
  code?: string;
  name?: string;
  description?: string;
  imageUrl?: string;
  sortOrder?: number;
  isActive?: boolean;
}

export interface ProductCategoryFilters {
  tenantId: string;
  parentId?: string | null;
  isActive?: boolean;
  search?: string;
}

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

export class ProductCategoryService {
  private repository: Repository<ProductCategory>;

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

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

    if (filters.parentId !== undefined) {
      where.parentId = filters.parentId === null ? IsNull() : filters.parentId;
    }

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

    const queryBuilder = this.repository.createQueryBuilder('category')
      .where(where)
      .leftJoinAndSelect('category.parent', 'parent')
      .orderBy('category.sortOrder', 'ASC')
      .addOrderBy('category.name', 'ASC');

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

    return queryBuilder.getMany();
  }

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

  async findByCode(code: string, tenantId: string): Promise<ProductCategory | null> {
    return this.repository.findOne({
      where: { code, tenantId },
    });
  }

  async findRootCategories(tenantId: string): Promise<ProductCategory[]> {
    return this.repository.find({
      where: { tenantId, parentId: IsNull() },
      order: { sortOrder: 'ASC', name: 'ASC' },
    });
  }

  async findChildren(parentId: string, tenantId: string): Promise<ProductCategory[]> {
    return this.repository.find({
      where: { parentId, tenantId },
      order: { sortOrder: 'ASC', name: 'ASC' },
    });
  }

  async create(data: CreateProductCategoryDto): Promise<ProductCategory> {
    let hierarchyPath = '';
    let hierarchyLevel = 0;

    if (data.parentId) {
      const parent = await this.findById(data.parentId, data.tenantId);
      if (parent) {
        hierarchyPath = parent.hierarchyPath
          ? `${parent.hierarchyPath}/${parent.id}`
          : parent.id;
        hierarchyLevel = parent.hierarchyLevel + 1;
      }
    }

    const category = this.repository.create({
      ...data,
      hierarchyPath,
      hierarchyLevel,
    });

    return this.repository.save(category);
  }

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

    if (data.parentId !== undefined && data.parentId !== category.parentId) {
      if (data.parentId === null) {
        data = { ...data, parentId: undefined } as UpdateProductCategoryDto;
        (category as any).hierarchyPath = '';
        (category as any).hierarchyLevel = 0;
      } else {
        const parent = await this.findById(data.parentId, tenantId);
        if (parent) {
          (category as any).hierarchyPath = parent.hierarchyPath
            ? `${parent.hierarchyPath}/${parent.id}`
            : parent.id;
          (category as any).hierarchyLevel = parent.hierarchyLevel + 1;
        }
      }
    }

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

  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 countProducts(categoryId: string, tenantId: string): Promise<number> {
    const result = await this.repository.manager.query(
      `SELECT COUNT(*) as count FROM products.products
       WHERE category_id = $1 AND tenant_id = $2 AND deleted_at IS NULL`,
      [categoryId, tenantId]
    );
    return parseInt(result[0].count, 10);
  }

  async getHierarchyTree(tenantId: string): Promise<ProductCategory[]> {
    const categories = await this.repository.find({
      where: { tenantId, isActive: true },
      order: { hierarchyLevel: 'ASC', sortOrder: 'ASC', name: 'ASC' },
    });

    return this.buildTree(categories);
  }

  private buildTree(categories: ProductCategory[], parentId: string | null = null): ProductCategory[] {
    return categories
      .filter(cat => cat.parentId === parentId)
      .map(cat => ({
        ...cat,
        children: this.buildTree(categories, cat.id),
      })) as ProductCategory[];
  }
}
