/**
 * ConceptoService - Catalogo de Conceptos de Obra
 *
 * Gestiona el catálogo jerárquico de conceptos de obra.
 * Los conceptos pueden tener estructura padre-hijo (niveles).
 *
 * @module Budgets
 */

import { Repository, IsNull } from 'typeorm';
import { BaseService, ServiceContext, PaginatedResult } from '../../../shared/services/base.service';
import { Concepto } from '../entities/concepto.entity';

export interface CreateConceptoDto {
  code: string;
  name: string;
  description?: string;
  parentId?: string;
  unitId?: string;
  unitPrice?: number;
  isComposite?: boolean;
}

export interface UpdateConceptoDto {
  name?: string;
  description?: string;
  unitId?: string;
  unitPrice?: number;
  isComposite?: boolean;
}

export class ConceptoService extends BaseService<Concepto> {
  constructor(repository: Repository<Concepto>) {
    super(repository);
  }

  /**
   * Crear un nuevo concepto con cálculo automático de nivel y path
   */
  async createConcepto(
    ctx: ServiceContext,
    data: CreateConceptoDto
  ): Promise<Concepto> {
    let level = 0;
    let path = data.code;

    if (data.parentId) {
      const parent = await this.findById(ctx, data.parentId);
      if (parent) {
        level = parent.level + 1;
        path = `${parent.path}/${data.code}`;
      }
    }

    return this.create(ctx, {
      ...data,
      level,
      path,
    });
  }

  /**
   * Obtener conceptos raíz (sin padre)
   */
  async findRootConceptos(
    ctx: ServiceContext,
    page = 1,
    limit = 50
  ): Promise<PaginatedResult<Concepto>> {
    return this.findAll(ctx, {
      page,
      limit,
      where: { parentId: IsNull() } as any,
    });
  }

  /**
   * Obtener hijos de un concepto
   */
  async findChildren(
    ctx: ServiceContext,
    parentId: string
  ): Promise<Concepto[]> {
    return this.find(ctx, {
      where: { parentId } as any,
      order: { code: 'ASC' },
    });
  }

  /**
   * Obtener árbol completo de conceptos
   */
  async getConceptoTree(
    ctx: ServiceContext,
    rootId?: string
  ): Promise<ConceptoNode[]> {
    const where = rootId
      ? { parentId: rootId }
      : { parentId: IsNull() };

    const roots = await this.find(ctx, {
      where: where as any,
      order: { code: 'ASC' },
    });

    return this.buildTree(ctx, roots);
  }

  private async buildTree(
    ctx: ServiceContext,
    conceptos: Concepto[]
  ): Promise<ConceptoNode[]> {
    const tree: ConceptoNode[] = [];

    for (const concepto of conceptos) {
      const children = await this.findChildren(ctx, concepto.id);
      const childNodes = children.length > 0
        ? await this.buildTree(ctx, children)
        : [];

      tree.push({
        ...concepto,
        children: childNodes,
      });
    }

    return tree;
  }

  /**
   * Buscar conceptos por código o nombre
   */
  async search(
    ctx: ServiceContext,
    term: string,
    limit = 20
  ): Promise<Concepto[]> {
    return this.repository
      .createQueryBuilder('c')
      .where('c.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('c.deleted_at IS NULL')
      .andWhere('(c.code ILIKE :term OR c.name ILIKE :term)', {
        term: `%${term}%`,
      })
      .orderBy('c.code', 'ASC')
      .take(limit)
      .getMany();
  }

  /**
   * Verificar si un código ya existe
   */
  async codeExists(ctx: ServiceContext, code: string): Promise<boolean> {
    return this.exists(ctx, { code } as any);
  }
}

interface ConceptoNode extends Concepto {
  children: ConceptoNode[];
}
