"use strict";
/**
 * ContractService - Servicio de gestión de contratos
 *
 * Gestión de contratos con workflow de aprobación.
 *
 * @module Contracts
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.ContractService = void 0;
const typeorm_1 = require("typeorm");
class ContractService {
    contractRepository;
    addendumRepository;
    constructor(contractRepository, addendumRepository) {
        this.contractRepository = contractRepository;
        this.addendumRepository = addendumRepository;
    }
    generateContractNumber(type) {
        const now = new Date();
        const year = now.getFullYear().toString().slice(-2);
        const month = (now.getMonth() + 1).toString().padStart(2, '0');
        const random = Math.random().toString(36).substring(2, 6).toUpperCase();
        const prefix = type === 'client' ? 'CTR' : 'SUB';
        return `${prefix}-${year}${month}-${random}`;
    }
    generateAddendumNumber(contractNumber, sequence) {
        return `${contractNumber}-ADD${sequence.toString().padStart(2, '0')}`;
    }
    async findWithFilters(ctx, filters = {}, page = 1, limit = 20) {
        const skip = (page - 1) * limit;
        const queryBuilder = this.contractRepository
            .createQueryBuilder('c')
            .leftJoinAndSelect('c.createdBy', 'createdBy')
            .leftJoinAndSelect('c.approvedBy', 'approvedBy')
            .where('c.tenant_id = :tenantId', { tenantId: ctx.tenantId })
            .andWhere('c.deleted_at IS NULL');
        if (filters.projectId) {
            queryBuilder.andWhere('c.project_id = :projectId', { projectId: filters.projectId });
        }
        if (filters.fraccionamientoId) {
            queryBuilder.andWhere('c.fraccionamiento_id = :fraccionamientoId', {
                fraccionamientoId: filters.fraccionamientoId,
            });
        }
        if (filters.contractType) {
            queryBuilder.andWhere('c.contract_type = :contractType', { contractType: filters.contractType });
        }
        if (filters.subcontractorId) {
            queryBuilder.andWhere('c.subcontractor_id = :subcontractorId', {
                subcontractorId: filters.subcontractorId,
            });
        }
        if (filters.status) {
            queryBuilder.andWhere('c.status = :status', { status: filters.status });
        }
        if (filters.expiringInDays) {
            const futureDate = new Date();
            futureDate.setDate(futureDate.getDate() + filters.expiringInDays);
            queryBuilder.andWhere('c.end_date <= :futureDate', { futureDate });
            queryBuilder.andWhere('c.end_date >= :today', { today: new Date() });
            queryBuilder.andWhere('c.status = :activeStatus', { activeStatus: 'active' });
        }
        queryBuilder
            .orderBy('c.created_at', 'DESC')
            .skip(skip)
            .take(limit);
        const [data, total] = await queryBuilder.getManyAndCount();
        return {
            data,
            meta: {
                total,
                page,
                limit,
                totalPages: Math.ceil(total / limit),
            },
        };
    }
    async findById(ctx, id) {
        return this.contractRepository.findOne({
            where: {
                id,
                tenantId: ctx.tenantId,
                deletedAt: null,
            },
        });
    }
    async findWithDetails(ctx, id) {
        return this.contractRepository.findOne({
            where: {
                id,
                tenantId: ctx.tenantId,
                deletedAt: null,
            },
            relations: ['createdBy', 'approvedBy', 'addendums'],
        });
    }
    async create(ctx, dto) {
        const contract = this.contractRepository.create({
            tenantId: ctx.tenantId,
            createdById: ctx.userId,
            contractNumber: this.generateContractNumber(dto.contractType),
            projectId: dto.projectId,
            fraccionamientoId: dto.fraccionamientoId,
            contractType: dto.contractType,
            clientContractType: dto.clientContractType,
            name: dto.name,
            description: dto.description,
            clientName: dto.clientName,
            clientRfc: dto.clientRfc?.toUpperCase(),
            clientAddress: dto.clientAddress,
            subcontractorId: dto.subcontractorId,
            specialty: dto.specialty,
            startDate: dto.startDate,
            endDate: dto.endDate,
            contractAmount: dto.contractAmount,
            currency: dto.currency || 'MXN',
            paymentTerms: dto.paymentTerms,
            retentionPercentage: dto.retentionPercentage || 5,
            advancePercentage: dto.advancePercentage || 0,
            notes: dto.notes,
            status: 'draft',
        });
        return this.contractRepository.save(contract);
    }
    async submitForReview(ctx, id) {
        const contract = await this.findById(ctx, id);
        if (!contract) {
            return null;
        }
        if (contract.status !== 'draft') {
            throw new Error('Can only submit draft contracts for review');
        }
        contract.status = 'review';
        contract.submittedAt = new Date();
        contract.submittedById = ctx.userId || '';
        contract.updatedById = ctx.userId || '';
        return this.contractRepository.save(contract);
    }
    async approveLegal(ctx, id) {
        const contract = await this.findById(ctx, id);
        if (!contract) {
            return null;
        }
        if (contract.status !== 'review') {
            throw new Error('Can only approve contracts in review');
        }
        contract.legalApprovedAt = new Date();
        contract.legalApprovedById = ctx.userId || '';
        contract.updatedById = ctx.userId || '';
        return this.contractRepository.save(contract);
    }
    async approve(ctx, id) {
        const contract = await this.findById(ctx, id);
        if (!contract) {
            return null;
        }
        if (contract.status !== 'review') {
            throw new Error('Can only approve contracts in review');
        }
        contract.status = 'approved';
        contract.approvedAt = new Date();
        contract.approvedById = ctx.userId || '';
        contract.updatedById = ctx.userId || '';
        return this.contractRepository.save(contract);
    }
    async activate(ctx, id, signedDocumentUrl) {
        const contract = await this.findById(ctx, id);
        if (!contract) {
            return null;
        }
        if (contract.status !== 'approved') {
            throw new Error('Can only activate approved contracts');
        }
        contract.status = 'active';
        contract.signedAt = new Date();
        if (signedDocumentUrl) {
            contract.signedDocumentUrl = signedDocumentUrl;
        }
        contract.updatedById = ctx.userId || '';
        return this.contractRepository.save(contract);
    }
    async complete(ctx, id) {
        const contract = await this.findById(ctx, id);
        if (!contract) {
            return null;
        }
        if (contract.status !== 'active') {
            throw new Error('Can only complete active contracts');
        }
        contract.status = 'completed';
        contract.updatedById = ctx.userId || '';
        return this.contractRepository.save(contract);
    }
    async terminate(ctx, id, reason) {
        const contract = await this.findById(ctx, id);
        if (!contract) {
            return null;
        }
        if (contract.status !== 'active') {
            throw new Error('Can only terminate active contracts');
        }
        contract.status = 'terminated';
        contract.terminatedAt = new Date();
        contract.terminationReason = reason;
        contract.updatedById = ctx.userId || '';
        return this.contractRepository.save(contract);
    }
    async updateProgress(ctx, id, progressPercentage, invoicedAmount, paidAmount) {
        const contract = await this.findById(ctx, id);
        if (!contract) {
            return null;
        }
        contract.progressPercentage = progressPercentage;
        if (invoicedAmount !== undefined) {
            contract.invoicedAmount = invoicedAmount;
        }
        if (paidAmount !== undefined) {
            contract.paidAmount = paidAmount;
        }
        contract.updatedById = ctx.userId || '';
        return this.contractRepository.save(contract);
    }
    async createAddendum(ctx, contractId, dto) {
        const contract = await this.findWithDetails(ctx, contractId);
        if (!contract) {
            throw new Error('Contract not found');
        }
        if (contract.status !== 'active') {
            throw new Error('Can only add addendums to active contracts');
        }
        const sequence = (contract.addendums?.length || 0) + 1;
        const addendum = this.addendumRepository.create({
            tenantId: ctx.tenantId,
            createdById: ctx.userId,
            contractId,
            addendumNumber: this.generateAddendumNumber(contract.contractNumber, sequence),
            addendumType: dto.addendumType,
            title: dto.title,
            description: dto.description,
            effectiveDate: dto.effectiveDate,
            newEndDate: dto.newEndDate,
            amountChange: dto.amountChange || 0,
            newContractAmount: dto.amountChange
                ? Number(contract.contractAmount) + Number(dto.amountChange)
                : undefined,
            scopeChanges: dto.scopeChanges,
            notes: dto.notes,
            status: 'draft',
        });
        return this.addendumRepository.save(addendum);
    }
    async approveAddendum(ctx, addendumId) {
        const addendum = await this.addendumRepository.findOne({
            where: {
                id: addendumId,
                tenantId: ctx.tenantId,
            },
            relations: ['contract'],
        });
        if (!addendum) {
            return null;
        }
        if (addendum.status !== 'draft' && addendum.status !== 'review') {
            throw new Error('Can only approve draft or review addendums');
        }
        addendum.status = 'approved';
        addendum.approvedAt = new Date();
        addendum.approvedById = ctx.userId || '';
        addendum.updatedById = ctx.userId || '';
        // Apply changes to contract
        if (addendum.newEndDate) {
            addendum.contract.endDate = addendum.newEndDate;
        }
        if (addendum.newContractAmount) {
            addendum.contract.contractAmount = addendum.newContractAmount;
        }
        await this.contractRepository.save(addendum.contract);
        return this.addendumRepository.save(addendum);
    }
    async softDelete(ctx, id) {
        const contract = await this.findById(ctx, id);
        if (!contract) {
            return false;
        }
        if (contract.status === 'active') {
            throw new Error('Cannot delete active contracts');
        }
        await this.contractRepository.update({ id, tenantId: ctx.tenantId }, { deletedAt: new Date(), deletedById: ctx.userId || '' });
        return true;
    }
    async getExpiringContracts(ctx, days = 30) {
        const futureDate = new Date();
        futureDate.setDate(futureDate.getDate() + days);
        return this.contractRepository.find({
            where: {
                tenantId: ctx.tenantId,
                status: 'active',
                endDate: (0, typeorm_1.LessThan)(futureDate),
                deletedAt: null,
            },
            order: { endDate: 'ASC' },
        });
    }
}
exports.ContractService = ContractService;
//# sourceMappingURL=contract.service.js.map