import { z } from 'zod';
import { CRLF } from '../../format/index.js';
import { CountryCodeEnum, CurrencyCodeEnum, } from '../../types/index.js';
import { formatField, formatNumericField } from '../format/encoder.js';
import { formatMonetaryAmount, formatOptionalMonetaryAmount, parseMonetaryAmount, } from '../format/monetary.js';
/**
 * B110 Record Schema - Account record
 * Fields 1400-1424 based on SHAAM 1.31 specification table
 */
export const B110Schema = z.object({
    // Field 1400: Record code (4) - Required - Alphanumeric
    code: z.literal('B110').describe('Record type code - always "B110"'),
    // Field 1401: Record number in file (9) - Required - Numeric
    recordNumber: z
        .number()
        .int()
        .min(1)
        .max(999_999_999)
        .describe('Sequential record number in file'),
    // Field 1402: Tax ID (9) - Required - Numeric
    vatId: z.string().min(1).max(9).regex(/^\d+$/).describe('Tax identification number'),
    // Field 1403: Account key (15) - Required - Alphanumeric
    accountKey: z.string().min(1).max(15).describe('Account key - must be unique'),
    // Field 1404: Account name (50) - Required - Alphanumeric
    accountName: z.string().max(50).optional().describe('Account name'),
    // Field 1405: Trial balance code (15) - Required - Alphanumeric
    trialBalanceCode: z.string().min(1).max(15).describe('Trial balance code'),
    // Field 1406: Trial balance code description (30) - Optional - Alphanumeric
    trialBalanceCodeDescription: z
        .string()
        .max(30)
        .optional()
        .describe('Trial balance code description'),
    // Field 1407: Customer/Supplier address - street (50) - Optional - Alphanumeric
    customerSupplierAddressStreet: z
        .string()
        .max(50)
        .optional()
        .describe('Customer/Supplier address - street (only for customer/supplier accounts)'),
    // Field 1408: Customer/Supplier address - house number (10) - Optional - Alphanumeric
    customerSupplierAddressHouseNumber: z
        .string()
        .max(10)
        .optional()
        .describe('Customer/Supplier address - house number (only for customer/supplier accounts)'),
    // Field 1409: Customer/Supplier address - city (30) - Optional - Alphanumeric
    customerSupplierAddressCity: z
        .string()
        .max(30)
        .optional()
        .describe('Customer/Supplier address - city (only for customer/supplier accounts)'),
    // Field 1410: Customer/Supplier address - ZIP (8) - Optional - Alphanumeric
    customerSupplierAddressZip: z
        .string()
        .max(8)
        .optional()
        .describe('Customer/Supplier address - ZIP (only for customer/supplier accounts)'),
    // Field 1411: Customer/Supplier address - country (30) - Optional - Alphanumeric
    customerSupplierAddressCountry: z
        .string()
        .max(30)
        .optional()
        .describe('Customer/Supplier address - country (only for customer/supplier accounts)'),
    // Field 1412: Country code (2) - Optional - Alphanumeric
    countryCode: CountryCodeEnum.optional().describe('Country code (only for customer/supplier accounts)'),
    // Field 1413: Parent account key (15) - Optional - Alphanumeric
    parentAccountKey: z.string().max(15).optional().describe('Parent account key'),
    // Field 1414: Account opening balance (15) - Optional - Numeric - Format: X9(12)V99
    accountOpeningBalance: z
        .number()
        .describe('Account opening balance (positive = debit, negative = credit)'),
    // Field 1415: Total debits (15) - Optional - Numeric - Format: X9(12)V99
    totalDebits: z.number().optional().describe('Total debits (excludes opening balance)'),
    // Field 1416: Total credits (15) - Optional - Numeric - Format: X9(12)V99
    totalCredits: z.number().optional().describe('Total credits (excludes opening balance)'),
    // Field 1417: Accounting classification code (4) - Conditional - Numeric
    accountingClassificationCode: z
        .number()
        .int()
        .min(0)
        .max(9999)
        .optional()
        .describe('Accounting classification code (mandatory if 6111 report required)'),
    // Field 1418: Reserved field (0) - Deprecated - Alphanumeric - Skipped as it has 0 length
    // Field 1419: Supplier/Customer tax ID (9) - Conditional - Numeric
    supplierCustomerTaxId: z
        .string()
        .max(9)
        .regex(/^$|^\d+$/)
        .optional()
        .describe('Supplier/Customer tax ID (required if double-entry bookkeeping, code 2 in field 1013)'),
    // Field 1420: Reserved field (0) - Deprecated - Alphanumeric - Skipped as it has 0 length
    // Field 1421: Branch ID (7) - Conditional - Alphanumeric
    branchId: z
        .string()
        .max(7)
        .optional()
        .describe('Branch ID (required if field 1034 = 1, see Note 3)'),
    // Field 1422: Opening balance in foreign currency (15) - Optional - Numeric - Format: X9(12)V99
    openingBalanceForeignCurrency: z
        .number()
        .optional()
        .describe('Opening balance in foreign currency'),
    // Field 1423: Foreign currency code (3) - Optional - Alphanumeric
    foreignCurrencyCode: CurrencyCodeEnum.optional().describe('Foreign currency code (refers to field 1422)'),
    // Field 1424: Reserved field (16) - Optional - Alphanumeric
    reserved: z.string().max(16).optional().describe('Reserved field for future use'),
});
/**
 * Encodes a B110 record to fixed-width string format
 * Total line width: 376 characters + CRLF
 */
export function encodeB110(input) {
    const fields = [
        formatField(input.code, 4, 'left'), // Field 1400: Record code (4)
        formatField(input.recordNumber.toString().padStart(9, '0'), 9, 'left'), // Field 1401: Record number (9)
        formatNumericField(input.vatId, 9), // Field 1402: VAT ID (9)
        formatField(input.accountKey, 15, 'left'), // Field 1403: Account key (15)
        formatField(input.accountName ?? '', 50, 'left'), // Field 1404: Account name (50)
        formatField(input.trialBalanceCode, 15, 'left'), // Field 1405: Trial balance code (15)
        formatField(input.trialBalanceCodeDescription ?? '', 30, 'left'), // Field 1406: Trial balance code description (30)
        formatField(input.customerSupplierAddressStreet ?? '', 50, 'left'), // Field 1407: Customer/Supplier address - street (50)
        formatField(input.customerSupplierAddressHouseNumber ?? '', 10, 'left'), // Field 1408: Customer/Supplier address - house number (10)
        formatField(input.customerSupplierAddressCity ?? '', 30, 'left'), // Field 1409: Customer/Supplier address - city (30)
        formatField(input.customerSupplierAddressZip ?? '', 8, 'left'), // Field 1410: Customer/Supplier address - ZIP (8)
        formatField(input.customerSupplierAddressCountry ?? '', 30, 'left'), // Field 1411: Customer/Supplier address - country (30)
        formatField(input.countryCode ?? '', 2, 'left'), // Field 1412: Country code (2)
        formatField(input.parentAccountKey ?? '', 15, 'left'), // Field 1413: Parent account key (15)
        formatOptionalMonetaryAmount(input.accountOpeningBalance) || formatMonetaryAmount(0), // Field 1414: Account opening balance (15)
        formatOptionalMonetaryAmount(input.totalDebits) || formatMonetaryAmount(0), // Field 1415: Total debits (15)
        formatOptionalMonetaryAmount(input.totalCredits) || formatMonetaryAmount(0), // Field 1416: Total credits (15)
        formatField(input.accountingClassificationCode?.toString().padStart(4, '0') ?? '0000', 4, 'left'), // Field 1417: Accounting classification code (4)
        // Field 1418: Reserved field (0) - skipped as it has 0 length
        // Use original value if available and looks like raw field data, otherwise format
        input.supplierCustomerTaxId
            ? input.supplierCustomerTaxId.length === 9
                ? input.supplierCustomerTaxId // Preserve original raw field data if exactly 9 chars
                : formatField(input.supplierCustomerTaxId, 9, 'left') // Right-pad with spaces for processed values
            : formatField('', 9, 'left'), // Field 1419: Supplier/Customer tax ID (9) - use spaces if empty
        // Field 1420: Reserved field (0) - skipped as it has 0 length
        formatField(input.branchId ?? '0', 7, 'left'), // Field 1421: Branch ID (7)
        formatOptionalMonetaryAmount(input.openingBalanceForeignCurrency) || formatMonetaryAmount(0), // Field 1422: Opening balance in foreign currency (15)
        formatField(input.foreignCurrencyCode ?? '', 3, 'left'), // Field 1423: Foreign currency code (3)
        formatField(input.reserved ?? '', 16, 'left'), // Field 1424: Reserved field (16)
    ];
    return fields.join('') + CRLF;
}
/**
 * Parses a fixed-width B110 record line back to object
 * Expected line length: 376 characters (excluding CRLF)
 */
export function parseB110(line) {
    // Remove CRLF if present
    const cleanLine = line.replace(/\r?\n$/, '');
    if (cleanLine.length !== 376) {
        throw new Error(`Invalid B110 record length: expected 376 characters, got ${cleanLine.length}`);
    }
    // Extract fields at their fixed positions
    let pos = 0;
    const code = cleanLine.slice(pos, pos + 4).trim();
    pos += 4;
    const recordNumber = parseInt(cleanLine
        .slice(pos, pos + 9)
        .trim()
        .replace(/^0+/, '') || '0', 10);
    pos += 9;
    const vatId = cleanLine
        .slice(pos, pos + 9)
        .trim()
        .replace(/^0+/, '') || '0';
    pos += 9;
    const accountKey = cleanLine.slice(pos, pos + 15).trim();
    pos += 15;
    const accountName = cleanLine.slice(pos, pos + 50).trim();
    pos += 50;
    const trialBalanceCode = cleanLine.slice(pos, pos + 15).trim();
    pos += 15;
    const trialBalanceCodeDescription = cleanLine.slice(pos, pos + 30).trim();
    pos += 30;
    const customerSupplierAddressStreet = cleanLine.slice(pos, pos + 50).trim();
    pos += 50;
    const customerSupplierAddressHouseNumber = cleanLine.slice(pos, pos + 10).trim();
    pos += 10;
    const customerSupplierAddressCity = cleanLine.slice(pos, pos + 30).trim();
    pos += 30;
    const customerSupplierAddressZip = cleanLine.slice(pos, pos + 8).trim();
    pos += 8;
    const customerSupplierAddressCountry = cleanLine.slice(pos, pos + 30).trim();
    pos += 30;
    const countryCodeRaw = cleanLine.slice(pos, pos + 2).trim();
    const countryCode = (countryCodeRaw || undefined);
    pos += 2;
    const parentAccountKey = cleanLine.slice(pos, pos + 15).trim();
    pos += 15;
    const accountOpeningBalance = cleanLine.slice(pos, pos + 15);
    pos += 15;
    const totalDebits = cleanLine.slice(pos, pos + 15);
    pos += 15;
    const totalCredits = cleanLine.slice(pos, pos + 15);
    pos += 15;
    const accountingClassificationCodeStr = cleanLine
        .slice(pos, pos + 4)
        .trim()
        .replace(/^0+/, '') || '';
    const accountingClassificationCode = accountingClassificationCodeStr === ''
        ? undefined
        : parseInt(accountingClassificationCodeStr, 10);
    pos += 4;
    // Field 1418: Reserved field (0) - skipped as it has 0 length
    const supplierCustomerTaxId = cleanLine
        .slice(pos, pos + 9)
        .trim()
        .replace(/^0+/, '') || '';
    pos += 9;
    // Field 1420: Reserved field (0) - skipped as it has 0 length
    const branchId = cleanLine.slice(pos, pos + 7).trim();
    pos += 7;
    const openingBalanceForeignCurrency = cleanLine.slice(pos, pos + 15);
    pos += 15;
    const foreignCurrencyCodeRaw = cleanLine.slice(pos, pos + 3).trim();
    const foreignCurrencyCode = (foreignCurrencyCodeRaw || undefined);
    pos += 3;
    const reserved = cleanLine.slice(pos, pos + 16).trim();
    // Validate the code field
    if (code !== 'B110') {
        throw new Error(`Invalid B110 record code: expected "B110", got "${code}"`);
    }
    const parsed = {
        code,
        recordNumber,
        vatId,
        accountKey,
        accountName,
        trialBalanceCode,
        trialBalanceCodeDescription,
        customerSupplierAddressStreet,
        customerSupplierAddressHouseNumber,
        customerSupplierAddressCity,
        customerSupplierAddressZip,
        customerSupplierAddressCountry,
        countryCode,
        parentAccountKey,
        accountOpeningBalance: parseMonetaryAmount(accountOpeningBalance),
        totalDebits: totalDebits.trim() ? parseMonetaryAmount(totalDebits) : undefined,
        totalCredits: totalCredits.trim() ? parseMonetaryAmount(totalCredits) : undefined,
        accountingClassificationCode,
        supplierCustomerTaxId,
        branchId,
        openingBalanceForeignCurrency: openingBalanceForeignCurrency.trim()
            ? parseMonetaryAmount(openingBalanceForeignCurrency)
            : undefined,
        foreignCurrencyCode,
        reserved,
    };
    // Validate against schema
    return B110Schema.parse(parsed);
}
//# sourceMappingURL=b110.js.map