/**
 * EstimacionService - Gestion de Estimaciones de Obra
 *
 * Gestiona estimaciones periodicas con workflow de aprobacion.
 * Incluye calculo de anticipos, retenciones e IVA.
 *
 * @module Estimates
 */

import { DataSource, Repository, IsNull } from 'typeorm';
import { Estimacion, EstimateStatus } from '../entities/estimacion.entity';
import { EstimacionConcepto } from '../entities/estimacion-concepto.entity';
import { Generador } from '../entities/generador.entity';
import { EstimacionWorkflow } from '../entities/estimacion-workflow.entity';

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

interface PaginatedResult<T> {
  data: T[];
  total: number;
  page: number;
  limit: number;
  totalPages: number;
}

export interface CreateEstimacionDto {
  contratoId: string;
  fraccionamientoId: string;
  periodStart: Date;
  periodEnd: Date;
  notes?: string;
}

export interface AddConceptoDto {
  conceptoId: string;
  contratoPartidaId?: string;
  quantityContract?: number;
  quantityPrevious?: number;
  quantityCurrent: number;
  unitPrice: number;
  notes?: string;
}

export interface AddGeneradorDto {
  generatorNumber: string;
  description?: string;
  loteId?: string;
  departamentoId?: string;
  locationDescription?: string;
  quantity: number;
  formula?: string;
  photoUrl?: string;
  sketchUrl?: string;
}

export interface EstimacionFilters {
  contratoId?: string;
  fraccionamientoId?: string;
  status?: EstimateStatus;
  periodFrom?: Date;
  periodTo?: Date;
}

interface ContractEstimateSummary {
  totalEstimates: number;
  totalApproved: number;
  totalPaid: number;
}

export class EstimacionService {
  private repository: Repository<Estimacion>;
  private conceptoRepository: Repository<EstimacionConcepto>;
  private generadorRepository: Repository<Generador>;
  private workflowRepository: Repository<EstimacionWorkflow>;

  constructor(private readonly dataSource: DataSource) {
    this.repository = dataSource.getRepository(Estimacion);
    this.conceptoRepository = dataSource.getRepository(EstimacionConcepto);
    this.generadorRepository = dataSource.getRepository(Generador);
    this.workflowRepository = dataSource.getRepository(EstimacionWorkflow);
  }

  /**
   * Busca una estimacion por ID
   */
  async findById(ctx: ServiceContext, id: string): Promise<Estimacion | null> {
    return this.repository.findOne({
      where: {
        id,
        tenantId: ctx.tenantId,
        deletedAt: IsNull(),
      },
    });
  }

  /**
   * Actualiza una estimacion
   */
  async update(
    ctx: ServiceContext,
    id: string,
    data: Partial<Estimacion>
  ): Promise<Estimacion | null> {
    const estimacion = await this.findById(ctx, id);
    if (!estimacion) {
      return null;
    }

    Object.assign(estimacion, data);
    estimacion.updatedById = ctx.userId ?? null;

    return this.repository.save(estimacion);
  }

  /**
   * Soft delete de una estimacion
   */
  async softDelete(ctx: ServiceContext, id: string): Promise<boolean> {
    const estimacion = await this.findById(ctx, id);
    if (!estimacion) {
      return false;
    }

    estimacion.deletedAt = new Date();
    estimacion.deletedById = ctx.userId ?? null;
    await this.repository.save(estimacion);
    return true;
  }

  /**
   * Crear nueva estimacion
   */
  async createEstimacion(
    ctx: ServiceContext,
    data: CreateEstimacionDto
  ): Promise<Estimacion> {
    const sequenceNumber = await this.getNextSequenceNumber(ctx, data.contratoId);
    const estimateNumber = await this.generateEstimateNumber(ctx, data.contratoId, sequenceNumber);

    const estimacion = this.repository.create({
      tenantId: ctx.tenantId,
      contratoId: data.contratoId,
      fraccionamientoId: data.fraccionamientoId,
      periodStart: data.periodStart,
      periodEnd: data.periodEnd,
      notes: data.notes,
      estimateNumber,
      sequenceNumber,
      status: 'draft' as EstimateStatus,
      createdById: ctx.userId,
    });

    const savedEstimacion = await this.repository.save(estimacion);

    // Registrar en workflow
    await this.addWorkflowEntry(ctx, savedEstimacion.id, null, 'draft', 'create', 'Estimacion creada');

    return savedEstimacion;
  }

  /**
   * Obtener siguiente numero de secuencia
   */
  private async getNextSequenceNumber(
    ctx: ServiceContext,
    contratoId: string
  ): Promise<number> {
    const result = await this.repository
      .createQueryBuilder('e')
      .select('MAX(e.sequence_number)', 'maxNumber')
      .where('e.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('e.contrato_id = :contratoId', { contratoId })
      .getRawOne();

    return (result?.maxNumber || 0) + 1;
  }

  /**
   * Generar numero de estimacion
   */
  private async generateEstimateNumber(
    _ctx: ServiceContext,
    contratoId: string,
    sequenceNumber: number
  ): Promise<string> {
    const year = new Date().getFullYear();
    return `EST-${year}-${contratoId.substring(0, 8).toUpperCase()}-${sequenceNumber.toString().padStart(3, '0')}`;
  }

  /**
   * Obtener estimaciones por contrato
   */
  async findByContrato(
    ctx: ServiceContext,
    contratoId: string,
    page = 1,
    limit = 20
  ): Promise<PaginatedResult<Estimacion>> {
    const qb = this.repository
      .createQueryBuilder('e')
      .where('e.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('e.contrato_id = :contratoId', { contratoId })
      .andWhere('e.deleted_at IS NULL');

    const skip = (page - 1) * limit;
    qb.orderBy('e.sequence_number', 'DESC').skip(skip).take(limit);

    const [data, total] = await qb.getManyAndCount();

    return {
      data,
      total,
      page,
      limit,
      totalPages: Math.ceil(total / limit),
    };
  }

  /**
   * Obtener estimaciones con filtros
   */
  async findWithFilters(
    ctx: ServiceContext,
    filters: EstimacionFilters,
    page = 1,
    limit = 20
  ): Promise<PaginatedResult<Estimacion>> {
    const qb = this.repository
      .createQueryBuilder('e')
      .where('e.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('e.deleted_at IS NULL');

    if (filters.contratoId) {
      qb.andWhere('e.contrato_id = :contratoId', { contratoId: filters.contratoId });
    }
    if (filters.fraccionamientoId) {
      qb.andWhere('e.fraccionamiento_id = :fraccionamientoId', { fraccionamientoId: filters.fraccionamientoId });
    }
    if (filters.status) {
      qb.andWhere('e.status = :status', { status: filters.status });
    }
    if (filters.periodFrom) {
      qb.andWhere('e.period_start >= :periodFrom', { periodFrom: filters.periodFrom });
    }
    if (filters.periodTo) {
      qb.andWhere('e.period_end <= :periodTo', { periodTo: filters.periodTo });
    }

    const skip = (page - 1) * limit;
    qb.orderBy('e.sequence_number', 'DESC').skip(skip).take(limit);

    const [data, total] = await qb.getManyAndCount();

    return {
      data,
      total,
      page,
      limit,
      totalPages: Math.ceil(total / limit),
    };
  }

  /**
   * Obtener estimacion con detalles completos
   */
  async findWithDetails(ctx: ServiceContext, id: string): Promise<Estimacion | null> {
    return this.repository.findOne({
      where: {
        id,
        tenantId: ctx.tenantId,
        deletedAt: IsNull(),
      },
      relations: [
        'conceptos',
        'conceptos.concepto',
        'conceptos.generadores',
        'amortizaciones',
        'retenciones',
        'workflow',
      ],
    });
  }

  /**
   * Agregar concepto a estimacion
   */
  async addConcepto(
    ctx: ServiceContext,
    estimacionId: string,
    data: AddConceptoDto
  ): Promise<EstimacionConcepto> {
    const estimacion = await this.findById(ctx, estimacionId);
    if (!estimacion || estimacion.status !== 'draft') {
      throw new Error('Cannot modify non-draft estimation');
    }

    const concepto = this.conceptoRepository.create({
      tenantId: ctx.tenantId,
      estimacionId,
      conceptoId: data.conceptoId,
      contratoPartidaId: data.contratoPartidaId,
      quantityContract: data.quantityContract || 0,
      quantityPrevious: data.quantityPrevious || 0,
      quantityCurrent: data.quantityCurrent,
      unitPrice: data.unitPrice,
      notes: data.notes,
      createdById: ctx.userId,
    });

    const savedConcepto = await this.conceptoRepository.save(concepto);
    await this.recalculateTotals(ctx, estimacionId);

    return savedConcepto;
  }

  /**
   * Agregar generador a concepto de estimacion
   */
  async addGenerador(
    ctx: ServiceContext,
    estimacionConceptoId: string,
    data: AddGeneradorDto
  ): Promise<Generador> {
    const concepto = await this.conceptoRepository.findOne({
      where: { id: estimacionConceptoId, tenantId: ctx.tenantId },
      relations: ['estimacion'],
    });

    if (!concepto) {
      throw new Error('Concepto not found');
    }

    const generador = this.generadorRepository.create({
      tenantId: ctx.tenantId,
      estimacionConceptoId,
      generatorNumber: data.generatorNumber,
      description: data.description,
      loteId: data.loteId,
      departamentoId: data.departamentoId,
      locationDescription: data.locationDescription,
      quantity: data.quantity,
      formula: data.formula,
      photoUrl: data.photoUrl,
      sketchUrl: data.sketchUrl,
      status: 'draft',
      capturedById: ctx.userId,
      createdById: ctx.userId,
    });

    return this.generadorRepository.save(generador);
  }

  /**
   * Recalcular totales de estimacion
   */
  async recalculateTotals(_ctx: ServiceContext, estimacionId: string): Promise<void> {
    // Ejecutar funcion de PostgreSQL
    await this.dataSource.query('SELECT estimates.calculate_estimate_totals($1)', [estimacionId]);
  }

  /**
   * Cambiar estado de estimacion
   */
  async changeStatus(
    ctx: ServiceContext,
    estimacionId: string,
    newStatus: EstimateStatus,
    action: string,
    comments?: string
  ): Promise<Estimacion | null> {
    const estimacion = await this.findById(ctx, estimacionId);
    if (!estimacion) {
      return null;
    }

    const validTransitions: Record<EstimateStatus, EstimateStatus[]> = {
      draft: ['submitted'],
      submitted: ['reviewed', 'rejected'],
      reviewed: ['approved', 'rejected'],
      approved: ['invoiced'],
      invoiced: ['paid'],
      paid: [],
      rejected: ['draft'],
      cancelled: [],
    };

    if (!validTransitions[estimacion.status]?.includes(newStatus)) {
      throw new Error(`Invalid status transition from ${estimacion.status} to ${newStatus}`);
    }

    const updateData: Partial<Estimacion> = { status: newStatus };

    switch (newStatus) {
      case 'submitted':
        updateData.submittedAt = new Date();
        updateData.submittedById = ctx.userId;
        break;
      case 'reviewed':
        updateData.reviewedAt = new Date();
        updateData.reviewedById = ctx.userId;
        break;
      case 'approved':
        updateData.approvedAt = new Date();
        updateData.approvedById = ctx.userId;
        break;
      case 'invoiced':
        updateData.invoicedAt = new Date();
        break;
      case 'paid':
        updateData.paidAt = new Date();
        break;
    }

    const updated = await this.update(ctx, estimacionId, updateData);

    // Registrar en workflow
    await this.addWorkflowEntry(ctx, estimacionId, estimacion.status, newStatus, action, comments);

    return updated;
  }

  /**
   * Agregar entrada al workflow
   */
  private async addWorkflowEntry(
    ctx: ServiceContext,
    estimacionId: string,
    fromStatus: EstimateStatus | null,
    toStatus: EstimateStatus,
    action: string,
    comments?: string
  ): Promise<void> {
    await this.workflowRepository.save(
      this.workflowRepository.create({
        tenantId: ctx.tenantId,
        estimacionId,
        fromStatus,
        toStatus,
        action,
        comments,
        performedById: ctx.userId,
        createdById: ctx.userId,
      })
    );
  }

  /**
   * Enviar estimacion para revision
   */
  async submit(ctx: ServiceContext, estimacionId: string): Promise<Estimacion | null> {
    return this.changeStatus(ctx, estimacionId, 'submitted', 'submit', 'Enviada para revision');
  }

  /**
   * Revisar estimacion
   */
  async review(ctx: ServiceContext, estimacionId: string): Promise<Estimacion | null> {
    return this.changeStatus(ctx, estimacionId, 'reviewed', 'review', 'Revision completada');
  }

  /**
   * Aprobar estimacion
   */
  async approve(ctx: ServiceContext, estimacionId: string): Promise<Estimacion | null> {
    return this.changeStatus(ctx, estimacionId, 'approved', 'approve', 'Aprobada');
  }

  /**
   * Rechazar estimacion
   */
  async reject(
    ctx: ServiceContext,
    estimacionId: string,
    reason: string
  ): Promise<Estimacion | null> {
    return this.changeStatus(ctx, estimacionId, 'rejected', 'reject', reason);
  }

  /**
   * Obtener resumen de estimaciones por contrato
   */
  async getContractSummary(ctx: ServiceContext, contratoId: string): Promise<ContractEstimateSummary> {
    const result = await this.repository
      .createQueryBuilder('e')
      .select([
        'COUNT(*) as total_estimates',
        'SUM(CASE WHEN e.status = \'approved\' THEN e.total_amount ELSE 0 END) as total_approved',
        'SUM(CASE WHEN e.status = \'paid\' THEN e.total_amount ELSE 0 END) as total_paid',
      ])
      .where('e.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('e.contrato_id = :contratoId', { contratoId })
      .andWhere('e.deleted_at IS NULL')
      .getRawOne();

    return {
      totalEstimates: parseInt(result?.total_estimates || '0'),
      totalApproved: parseFloat(result?.total_approved || '0'),
      totalPaid: parseFloat(result?.total_paid || '0'),
    };
  }

  /**
   * Obtener totales acumulados hasta una estimacion
   */
  async getCumulativeTotals(
    ctx: ServiceContext,
    contratoId: string,
    upToSequenceNumber: number
  ): Promise<CumulativeTotals> {
    const result = await this.repository
      .createQueryBuilder('e')
      .select([
        'SUM(e.subtotal) as cumulative_subtotal',
        'SUM(e.advance_amount) as cumulative_advance',
        'SUM(e.retention_amount) as cumulative_retention',
        'SUM(e.tax_amount) as cumulative_tax',
        'SUM(e.total_amount) as cumulative_total',
      ])
      .where('e.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('e.contrato_id = :contratoId', { contratoId })
      .andWhere('e.sequence_number <= :sequenceNumber', { sequenceNumber: upToSequenceNumber })
      .andWhere('e.deleted_at IS NULL')
      .andWhere('e.status NOT IN (:...excludedStatuses)', {
        excludedStatuses: ['cancelled', 'rejected'],
      })
      .getRawOne();

    return {
      cumulativeSubtotal: parseFloat(result?.cumulative_subtotal || '0'),
      cumulativeAdvance: parseFloat(result?.cumulative_advance || '0'),
      cumulativeRetention: parseFloat(result?.cumulative_retention || '0'),
      cumulativeTax: parseFloat(result?.cumulative_tax || '0'),
      cumulativeTotal: parseFloat(result?.cumulative_total || '0'),
    };
  }

  /**
   * Obtener cantidades previas acumuladas para un concepto
   */
  async getPreviousQuantities(
    ctx: ServiceContext,
    contratoId: string,
    conceptoId: string,
    beforeSequenceNumber: number
  ): Promise<PreviousQuantities> {
    const result = await this.conceptoRepository
      .createQueryBuilder('ec')
      .innerJoin('ec.estimacion', 'e')
      .select([
        'SUM(ec.quantity_current) as total_previous',
        'SUM(ec.amount_current) as total_previous_amount',
      ])
      .where('e.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('e.contrato_id = :contratoId', { contratoId })
      .andWhere('ec.concepto_id = :conceptoId', { conceptoId })
      .andWhere('e.sequence_number < :sequenceNumber', { sequenceNumber: beforeSequenceNumber })
      .andWhere('e.deleted_at IS NULL')
      .andWhere('e.status NOT IN (:...excludedStatuses)', {
        excludedStatuses: ['cancelled', 'rejected'],
      })
      .getRawOne();

    return {
      totalQuantity: parseFloat(result?.total_previous || '0'),
      totalAmount: parseFloat(result?.total_previous_amount || '0'),
    };
  }

  /**
   * Calcular amortizacion de anticipo para una estimacion
   */
  async calculateAdvanceAmortization(
    ctx: ServiceContext,
    estimacionId: string,
    advancePercentage: number,
    maxAdvanceAmount: number
  ): Promise<AmortizationCalculation> {
    const estimacion = await this.findById(ctx, estimacionId);
    if (!estimacion) {
      throw new Error('Estimacion no encontrada');
    }

    const cumulativeTotals = await this.getCumulativeTotals(
      ctx,
      estimacion.contratoId,
      estimacion.sequenceNumber - 1
    );

    const currentSubtotal = Number(estimacion.subtotal) || 0;
    const calculatedAmortization = currentSubtotal * (advancePercentage / 100);
    const previouslyAmortized = cumulativeTotals.cumulativeAdvance;
    const remainingToAmortize = maxAdvanceAmount - previouslyAmortized;
    const actualAmortization = Math.min(calculatedAmortization, remainingToAmortize);

    return {
      calculatedAmortization,
      previouslyAmortized,
      remainingToAmortize,
      actualAmortization,
      isFullyAmortized: actualAmortization >= remainingToAmortize,
    };
  }

  /**
   * Obtener historial de workflow de una estimacion
   */
  async getWorkflowHistory(
    ctx: ServiceContext,
    estimacionId: string
  ): Promise<EstimacionWorkflow[]> {
    return this.workflowRepository.find({
      where: {
        tenantId: ctx.tenantId,
        estimacionId,
      },
      order: { performedAt: 'ASC' },
      relations: ['performedBy'],
    });
  }

  /**
   * Cancelar estimacion
   */
  async cancel(
    ctx: ServiceContext,
    estimacionId: string,
    reason: string
  ): Promise<Estimacion | null> {
    const estimacion = await this.findById(ctx, estimacionId);
    if (!estimacion) {
      return null;
    }

    if (estimacion.status === 'paid' || estimacion.status === 'invoiced') {
      throw new Error('Cannot cancel paid or invoiced estimations');
    }

    const updateData: Partial<Estimacion> = {
      status: 'cancelled' as EstimateStatus,
    };

    const updated = await this.update(ctx, estimacionId, updateData);
    await this.addWorkflowEntry(ctx, estimacionId, estimacion.status, 'cancelled', 'cancel', reason);

    return updated;
  }

  /**
   * Duplicar estimacion (crear nueva basada en anterior)
   */
  async duplicate(
    ctx: ServiceContext,
    sourceEstimacionId: string,
    newPeriodStart: Date,
    newPeriodEnd: Date
  ): Promise<Estimacion> {
    const source = await this.findWithDetails(ctx, sourceEstimacionId);
    if (!source) {
      throw new Error('Estimacion origen no encontrada');
    }

    const newEstimacion = await this.createEstimacion(ctx, {
      contratoId: source.contratoId,
      fraccionamientoId: source.fraccionamientoId,
      periodStart: newPeriodStart,
      periodEnd: newPeriodEnd,
      notes: `Duplicada de ${source.estimateNumber}`,
    });

    if (source.conceptos && source.conceptos.length > 0) {
      for (const concepto of source.conceptos) {
        const previousQuantities = await this.getPreviousQuantities(
          ctx,
          source.contratoId,
          concepto.conceptoId,
          newEstimacion.sequenceNumber
        );

        await this.addConcepto(ctx, newEstimacion.id, {
          conceptoId: concepto.conceptoId,
          contratoPartidaId: concepto.contratoPartidaId ?? undefined,
          quantityContract: Number(concepto.quantityContract) || 0,
          quantityPrevious: previousQuantities.totalQuantity,
          quantityCurrent: 0,
          unitPrice: Number(concepto.unitPrice) || 0,
          notes: concepto.notes ?? undefined,
        });
      }
    }

    return newEstimacion;
  }

  /**
   * Generar datos para documento de estimacion
   */
  async generateDocumentData(
    ctx: ServiceContext,
    estimacionId: string
  ): Promise<EstimacionDocumentData> {
    const estimacion = await this.findWithDetails(ctx, estimacionId);
    if (!estimacion) {
      throw new Error('Estimacion no encontrada');
    }

    const cumulativeTotals = await this.getCumulativeTotals(
      ctx,
      estimacion.contratoId,
      estimacion.sequenceNumber
    );

    const previousTotals = await this.getCumulativeTotals(
      ctx,
      estimacion.contratoId,
      estimacion.sequenceNumber - 1
    );

    return {
      estimacion,
      currentPeriod: {
        subtotal: Number(estimacion.subtotal) || 0,
        advanceAmount: Number(estimacion.advanceAmount) || 0,
        retentionAmount: Number(estimacion.retentionAmount) || 0,
        taxAmount: Number(estimacion.taxAmount) || 0,
        totalAmount: Number(estimacion.totalAmount) || 0,
      },
      previousAccumulated: previousTotals,
      totalAccumulated: cumulativeTotals,
    };
  }

  /**
   * Obtener estadisticas de estimaciones por periodo
   */
  async getEstimacionStats(
    ctx: ServiceContext,
    filters: EstimacionFilters
  ): Promise<EstimacionStats> {
    const qb = this.repository
      .createQueryBuilder('e')
      .where('e.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('e.deleted_at IS NULL');

    if (filters.contratoId) {
      qb.andWhere('e.contrato_id = :contratoId', { contratoId: filters.contratoId });
    }
    if (filters.fraccionamientoId) {
      qb.andWhere('e.fraccionamiento_id = :fraccionamientoId', {
        fraccionamientoId: filters.fraccionamientoId,
      });
    }
    if (filters.periodFrom) {
      qb.andWhere('e.period_start >= :periodFrom', { periodFrom: filters.periodFrom });
    }
    if (filters.periodTo) {
      qb.andWhere('e.period_end <= :periodTo', { periodTo: filters.periodTo });
    }

    const estimaciones = await qb.getMany();

    const stats: EstimacionStats = {
      totalCount: estimaciones.length,
      byStatus: {},
      totalSubtotal: 0,
      totalAdvance: 0,
      totalRetention: 0,
      totalTax: 0,
      totalAmount: 0,
    };

    estimaciones.forEach((e) => {
      stats.byStatus[e.status] = (stats.byStatus[e.status] || 0) + 1;
      stats.totalSubtotal += Number(e.subtotal) || 0;
      stats.totalAdvance += Number(e.advanceAmount) || 0;
      stats.totalRetention += Number(e.retentionAmount) || 0;
      stats.totalTax += Number(e.taxAmount) || 0;
      stats.totalAmount += Number(e.totalAmount) || 0;
    });

    return stats;
  }

  /**
   * Actualizar concepto de estimacion
   */
  async updateConcepto(
    ctx: ServiceContext,
    conceptoId: string,
    data: Partial<AddConceptoDto>
  ): Promise<EstimacionConcepto | null> {
    const concepto = await this.conceptoRepository.findOne({
      where: { id: conceptoId, tenantId: ctx.tenantId },
      relations: ['estimacion'],
    });

    if (!concepto) {
      return null;
    }

    if (concepto.estimacion.status !== 'draft') {
      throw new Error('Cannot modify conceptos of non-draft estimations');
    }

    if (data.quantityCurrent !== undefined) {
      concepto.quantityCurrent = data.quantityCurrent;
    }
    if (data.quantityPrevious !== undefined) {
      concepto.quantityPrevious = data.quantityPrevious;
    }
    if (data.unitPrice !== undefined) {
      concepto.unitPrice = data.unitPrice;
    }
    if (data.notes !== undefined) {
      concepto.notes = data.notes ?? null;
    }

    concepto.updatedById = ctx.userId ?? null;
    const saved = await this.conceptoRepository.save(concepto);
    await this.recalculateTotals(ctx, concepto.estimacionId);

    return saved;
  }

  /**
   * Eliminar concepto de estimacion
   */
  async removeConcepto(ctx: ServiceContext, conceptoId: string): Promise<boolean> {
    const concepto = await this.conceptoRepository.findOne({
      where: { id: conceptoId, tenantId: ctx.tenantId },
      relations: ['estimacion'],
    });

    if (!concepto) {
      return false;
    }

    if (concepto.estimacion.status !== 'draft') {
      throw new Error('Cannot remove conceptos from non-draft estimations');
    }

    const estimacionId = concepto.estimacionId;
    concepto.deletedAt = new Date();
    concepto.deletedById = ctx.userId ?? null;
    await this.conceptoRepository.save(concepto);
    await this.recalculateTotals(ctx, estimacionId);

    return true;
  }
}

/**
 * Interfaces adicionales para resultados
 */
export interface CumulativeTotals {
  cumulativeSubtotal: number;
  cumulativeAdvance: number;
  cumulativeRetention: number;
  cumulativeTax: number;
  cumulativeTotal: number;
}

export interface PreviousQuantities {
  totalQuantity: number;
  totalAmount: number;
}

export interface AmortizationCalculation {
  calculatedAmortization: number;
  previouslyAmortized: number;
  remainingToAmortize: number;
  actualAmortization: number;
  isFullyAmortized: boolean;
}

export interface EstimacionDocumentData {
  estimacion: Estimacion;
  currentPeriod: {
    subtotal: number;
    advanceAmount: number;
    retentionAmount: number;
    taxAmount: number;
    totalAmount: number;
  };
  previousAccumulated: CumulativeTotals;
  totalAccumulated: CumulativeTotals;
}

export interface EstimacionStats {
  totalCount: number;
  byStatus: Partial<Record<EstimateStatus, number>>;
  totalSubtotal: number;
  totalAdvance: number;
  totalRetention: number;
  totalTax: number;
  totalAmount: number;
}
