/**
 * Main API for generating SHAAM uniform format reports
 */
import { assembleFile } from '../generator/format/encoder.js';
import { encodeA000, encodeA000Sum, encodeA100, encodeB100, encodeB110, encodeC100, encodeD110, encodeD120, encodeM100, encodeZ900, } from '../generator/records/index.js';
import { ShaamFormatError } from '../validation/errors.js';
import { validateInput } from '../validation/validate-input.js';
/**
 * Generates SHAAM uniform format report files (INI.TXT and BKMVDATA.TXT)
 * from a high-level JSON input object.
 *
 * @param input - The report input data
 * @param options - Generation options
 * @returns Generated SHAAM format files and metadata
 */
export function generateUniformFormatReport(input, options = {}) {
    // Validate input data first
    const validationMode = options.validationMode || 'fail-fast';
    const validationErrors = validateInput(input, validationMode);
    if (validationErrors.length > 0) {
        if (validationMode === 'fail-fast') {
            // This should have already thrown, but just in case
            throw new ShaamFormatError('Validation failed', validationErrors);
        }
        // For collect-all mode, we still throw if there are errors
        // The user can catch and inspect the errors if needed
        throw new ShaamFormatError('Input validation failed', validationErrors);
    }
    const records = [];
    const iniRecords = [];
    const recordCounts = {};
    let recordNumber = 1;
    // Helper function to count and add data record
    const addRecord = (recordType, recordContent) => {
        records.push(recordContent);
        recordCounts[recordType] = (recordCounts[recordType] || 0) + 1;
        recordNumber++;
    };
    // Helper function to add INI record
    const addIniRecord = (recordType, recordContent) => {
        iniRecords.push(recordContent);
        recordCounts[recordType] = (recordCounts[recordType] || 0) + 1;
    };
    // Calculate expected BKMVDATA record counts based on input data
    const expectedDataRecordCount = 1 + // A100 record
        input.documents.length * 3 + // C100, D110, D120 for each document
        input.journalEntries.length + // B100 for each journal entry
        input.accounts.length + // B110 for each account
        input.inventory.length + // M100 for each inventory item
        1; // Z900 record
    // Generate A000 record for INI.TXT
    const fileHeaderRecord = {
        totalRecords: expectedDataRecordCount.toString(), // Actual count of BKMVDATA records
        vatId: input.business.taxId,
        softwareRegNumber: '12345678', // TODO: Use actual software registration number
        softwareName: 'SHAAM Generator',
        softwareVersion: '1.0.0',
        vendorVatId: '123456782', // TODO: Use actual vendor VAT ID
        vendorName: 'Accounter',
        softwareType: '2', // Multi-year software
        fileOutputPath: options.fileNameBase || `C:\\OPENFRMT\\${input.business.taxId}.`,
        accountingType: '2', // Double-entry accounting
        balanceRequired: '2',
        companyRegId: '',
        withholdingFileNum: '',
        businessName: input.business.name,
        businessStreet: input.business.address?.street ?? '',
        businessHouseNum: input.business.address?.houseNumber ?? '',
        businessCity: input.business.address?.city ?? '',
        businessZip: input.business.address?.zip ?? '',
        taxYear: '    ', // YYYY format. not required for now
        startDate: input.business.reportingPeriod.startDate.replace(/-/g, ''),
        endDate: input.business.reportingPeriod.endDate.replace(/-/g, ''),
        processStartDate: new Date().toISOString().slice(0, 10).replace(/-/g, ''),
        processStartTime: new Date().toTimeString().slice(0, 5).replace(':', ''),
        languageCode: '0', // Hebrew
        characterEncoding: '1', // ISO-8859-8-i
        compressionSoftware: 'zip', // TODO:replace with a real compression software
        baseCurrency: 'ILS',
        branchInfoFlag: '0', // No branches for now
    };
    addIniRecord('A000', encodeA000(fileHeaderRecord));
    // Add A000Sum records for each record type to INI.TXT
    // Calculate expected counts based on input data
    const expectedCounts = {
        A100: 1,
        C100: input.documents.length,
        D110: input.documents.length,
        D120: input.documents.length,
        B100: input.journalEntries.length,
        B110: input.accounts.length,
        M100: input.inventory.length,
        Z900: 1,
    };
    // Add summary records for each record type
    for (const [recordType, count] of Object.entries(expectedCounts)) {
        if (count > 0) {
            const summaryRecord = {
                code: recordType,
                recordCount: count.toString(),
            };
            addIniRecord('A000Sum', encodeA000Sum(summaryRecord));
        }
    }
    // 1. Business metadata - A100 record
    const businessMetadata = {
        recordNumber,
        vatId: input.business.taxId,
        reserved: '',
    };
    addRecord('A100', encodeA100(businessMetadata));
    // TODO: 2. Summaries... (not implemented yet)
    // 3. For each document in input.documents
    for (const document of input.documents) {
        // Document header - C100 record
        const documentRecord = {
            code: 'C100',
            recordNumber: recordNumber.toString(),
            vatId: input.business.taxId,
            documentType: document.type,
            documentId: document.id,
            documentIssueDate: document.date.replace(/-/g, ''), // Convert YYYY-MM-DD to YYYYMMDD
            documentIssueTime: '',
            customerName: '',
            customerStreet: '',
            customerHouseNumber: '',
            customerCity: '',
            customerPostCode: '',
            customerCountry: '',
            customerCountryCode: '',
            customerPhone: '',
            customerVatId: '',
            documentValueDate: '',
            foreignCurrencyAmount: '',
            currencyCode: '',
            amountBeforeDiscount: '',
            documentDiscount: '',
            amountAfterDiscountExcludingVat: '',
            vatAmount: '',
            amountIncludingVat: document.amount.toFixed(2), // Format as decimal with 2 places
            withholdingTaxAmount: '',
            customerKey: '',
            matchingField: '',
            cancelledAttribute1: '',
            cancelledDocument: '',
            cancelledAttribute2: '',
            documentDate: '',
            branchKey: '',
            cancelledAttribute3: '',
            actionExecutor: '',
            lineConnectingField: '',
            reserved: '',
        };
        addRecord('C100', encodeC100(documentRecord));
        // Document line - D110 record
        const documentLineRecord = {
            code: 'D110',
            recordNumber: recordNumber.toString(),
            vatId: input.business.taxId,
            documentType: document.type,
            documentNumber: document.id,
            lineNumber: '1',
            baseDocumentType: '',
            baseDocumentNumber: '',
            transactionType: '',
            internalCatalogCode: '',
            goodsServiceDescription: document.description || 'Item',
            manufacturerName: '',
            serialNumber: '',
            unitOfMeasureDescription: '',
            quantity: '1',
            unitPriceExcludingVat: '',
            lineDiscount: '',
            lineTotal: '',
            vatRatePercent: '',
            reserved1: '',
            branchId: '1',
            reserved2: '',
            documentDate: document.date.replace(/-/g, ''),
            headerLinkField: '',
            baseDocumentBranchId: '',
            reserved3: '',
        };
        addRecord('D110', encodeD110(documentLineRecord));
        // Payment record - D120 record (if applicable)
        const paymentRecord = {
            code: 'D120',
            recordNumber: recordNumber.toString(),
            vatId: input.business.taxId,
            documentType: document.type,
            documentNumber: document.id,
            lineNumber: '1',
            paymentMethod: '1', // Default payment method
            bankNumber: '',
            branchNumber: '',
            accountNumber: '',
            checkNumber: '',
            paymentDueDate: '',
            lineAmount: document.amount.toFixed(2), // Format as decimal with 2 places
            acquirerCode: '',
            cardBrand: '',
            creditTransactionType: '',
            firstPaymentAmount: '',
            installmentsCount: '',
            additionalPaymentAmount: '',
            reserved1: '',
            branchId: '1',
            reserved2: '',
            documentDate: document.date.replace(/-/g, ''),
            headerLinkField: '',
            reserved: '',
        };
        addRecord('D120', encodeD120(paymentRecord));
    }
    // 4. Journal lines: for each entry and its lines
    for (const entry of input.journalEntries) {
        const journalRecord = {
            code: 'B100',
            recordNumber,
            vatId: input.business.taxId,
            transactionNumber: entry.transactionNumber ?? parseInt(entry.id.replace(/\D/g, '') || '1', 10), // Use preserved value or extract from ID
            transactionLineNumber: entry.transactionLineNumber ?? 1,
            batchNumber: entry.batchNumber, // Use preserved batch number
            transactionType: entry.transactionType ?? '',
            referenceDocument: entry.referenceDocument ?? '',
            referenceDocumentType: entry.referenceDocumentType,
            referenceDocument2: entry.referenceDocument2 ?? '',
            referenceDocumentType2: entry.referenceDocumentType2,
            details: entry.description || '',
            date: entry.date.replace(/-/g, ''),
            valueDate: entry.valueDate ? entry.valueDate.replace(/-/g, '') : entry.date.replace(/-/g, ''), // Use preserved or fallback to date
            accountKey: entry.accountId,
            counterAccountKey: entry.counterAccountKey ?? '',
            debitCreditIndicator: entry.debitCreditIndicator ?? (entry.amount >= 0 ? '1' : '2'),
            currencyCode: entry.currencyCode,
            transactionAmount: entry.transactionAmount ?? entry.amount, // Use signed value (reverted to preserve fixture behavior)
            foreignCurrencyAmount: entry.foreignCurrencyAmount, // Use preserved value as-is
            quantityField: entry.quantityField, // Use preserved value
            matchingField1: entry.matchingField1 ?? '',
            matchingField2: entry.matchingField2 ?? '',
            branchId: entry.branchId ?? '',
            entryDate: entry.entryDate ? entry.entryDate.replace(/-/g, '') : entry.date.replace(/-/g, ''), // Use preserved or fallback to date
            operatorUsername: entry.operatorUsername ?? '',
            reserved: entry.reserved ?? '',
        };
        addRecord('B100', encodeB100(journalRecord));
    }
    // 5. Accounts: B110 records
    for (const account of input.accounts) {
        const accountRecord = {
            code: 'B110',
            recordNumber,
            vatId: input.business.taxId,
            accountKey: account.id,
            accountName: account.name,
            trialBalanceCode: account.sortCode.key,
            trialBalanceCodeDescription: account.sortCode.name,
            customerSupplierAddressStreet: account.address?.street,
            customerSupplierAddressHouseNumber: account.address?.houseNumber,
            customerSupplierAddressCity: account.address?.city,
            customerSupplierAddressZip: account.address?.zip,
            customerSupplierAddressCountry: account.address?.country,
            countryCode: account.countryCode, // Cast to CountryCodeEnum type
            parentAccountKey: account.parentAccountKey,
            accountOpeningBalance: account.accountOpeningBalance,
            totalDebits: account.totalDebits,
            totalCredits: account.totalCredits,
            accountingClassificationCode: account.accountingClassificationCode
                ? (() => {
                    // Extract numeric part from alphanumeric code (e.g., 'A1100' -> 1100)
                    const numericPart = account.accountingClassificationCode.replace(/\D/g, '');
                    const parsed = parseInt(numericPart, 10);
                    return Number.isNaN(parsed) ? undefined : Math.min(parsed, 9999); // Cap at max value
                })()
                : undefined,
            supplierCustomerTaxId: account.vatId
                ? (() => {
                    // Remove all spaces and validate that only digits remain
                    const digitsOnly = account.vatId.replace(/\s/g, '');
                    if (digitsOnly && !/^\d+$/.test(digitsOnly)) {
                        throw new Error(`Invalid vatId for account ${account.id}: "${account.vatId}" contains non-digit characters. Only digits and spaces are allowed.`);
                    }
                    if (digitsOnly && digitsOnly.length > 0) {
                        return digitsOnly.length > 9 ? digitsOnly.substring(0, 9) : digitsOnly;
                    }
                    return undefined;
                })()
                : undefined,
            branchId: account.branchId,
            openingBalanceForeignCurrency: account.openingBalanceForeignCurrency,
            foreignCurrencyCode: account.foreignCurrencyCode,
            reserved: undefined,
        };
        addRecord('B110', encodeB110(accountRecord));
    }
    // 6. Inventory: M100 records
    for (const item of input.inventory) {
        const inventoryRecord = {
            code: 'M100',
            recordNumber: recordNumber.toString(),
            vatId: input.business.taxId,
            universalItemCode: '',
            supplierItemCode: '',
            internalItemCode: item.id,
            itemName: item.name,
            classificationCode: '',
            classificationDescription: '',
            unitOfMeasure: '',
            openingStock: '0',
            totalStockIn: '0',
            totalStockOut: item.quantity.toString(),
            endPeriodCostNonBonded: '',
            endPeriodCostBonded: '',
            reserved: '',
        };
        addRecord('M100', encodeM100(inventoryRecord));
    }
    // 7. Closing record: Z900
    const closingRecord = {
        recordNumber,
        vatId: input.business.taxId,
        totalRecords: records.length + 1, // Count of records before Z900 is added
        reserved: '',
    };
    addRecord('Z900', encodeZ900(closingRecord));
    // Build iniText and dataText using the assembler
    const iniText = assembleFile(iniRecords);
    const dataText = assembleFile(records);
    // Create virtual File objects
    const iniFile = new File([new Blob([iniText])], `${options.fileNameBase || 'report'}.INI.TXT`, {
        type: 'text/plain;charset=windows-1255',
    });
    const dataFile = new File([new Blob([dataText])], `${options.fileNameBase || 'report'}.BKMVDATA.TXT`, {
        type: 'text/plain;charset=windows-1255',
    });
    return {
        iniText,
        dataText,
        iniFile,
        dataFile,
        summary: {
            totalRecords: records.length + iniRecords.length,
            perType: recordCounts,
        },
    };
}
//# sourceMappingURL=generate-report.js.map