import { z } from 'zod';
import { ALLOWED_BALANCE_SHEET_CODES, BALANCE_SHEET_CODE_NAMES, NEGATIVE_ALLOWED_BALANCE_SHEET_CODES, SECTION_1_21, SECTION_1_27, SECTION_1_28, SECTION_1_30, SECTION_1_32, SECTION_1_33, SECTION_1_36, SECTION_1_37, SECTION_1_38, SECTION_1_39, SECTION_1_40, SECTION_1_41, SECTION_1_43, SECTION_1_44, SECTION_1_45, SECTION_1_46, SECTION_1_48, SECTION_1_50, SECTION_1_51, } from '../types/index.js';
// Create a set for faster lookup of allowed codes
const allowedCodesSet = new Set(ALLOWED_BALANCE_SHEET_CODES);
const negativeAllowedCodesSet = new Set(NEGATIVE_ALLOWED_BALANCE_SHEET_CODES);
function codeToName(code) {
    return `${BALANCE_SHEET_CODE_NAMES[code] ?? 'Unknown code'} (${code})`;
}
// Zod schema for a single balance sheet record
const balanceSheetRecordSchema = z
    .object({
    code: z
        .number()
        .int()
        .superRefine((value, ctx) => {
        if (!allowedCodesSet.has(value)) {
            ctx.addIssue({
                code: z.ZodIssueCode.custom,
                message: `Invalid balance sheet code: ${value}`,
                path: ['code'],
            });
        }
    }),
    amount: z.number().int(),
})
    .superRefine((record, ctx) => {
    // Validate that non-negative codes don't have negative amounts
    if (!negativeAllowedCodesSet.has(record.code) &&
        record.amount < 0) {
        ctx.addIssue({
            code: z.ZodIssueCode.custom,
            message: `Code ${codeToName(record.code)} does not allow negative amounts`,
            path: ['amount'],
        });
    }
});
function cumulativeCodeValidationFormula(code, sectionCodes, subtractSectionCodes) {
    return (map) => {
        const sectionAmount = sectionCodes
            .filter(sectionCode => sectionCode !== code)
            .map(sectionCode => map.get(sectionCode) || 0)
            .reduce((a, b) => a + b, 0);
        const subtractAmount = subtractSectionCodes
            ? subtractSectionCodes
                .filter(sectionCode => sectionCode !== code)
                .map(sectionCode => map.get(sectionCode) || 0)
                .reduce((a, b) => a + b, 0)
            : 0;
        return sectionAmount - subtractAmount;
    };
}
function commonCumulativeCodeValidation(code, sectionCodes, subtractSectionCodes) {
    return {
        formula: cumulativeCodeValidationFormula(code, sectionCodes, subtractSectionCodes),
        description: 'Sum of all subsections',
    };
}
// Define the validation logic for each summary code
const summaryValidations = {
    7000: commonCumulativeCodeValidation(7000, [7100, 7200, 7300, 7400, 7600, 7700, 7800]),
    7100: commonCumulativeCodeValidation(7100, SECTION_1_27),
    7200: commonCumulativeCodeValidation(7200, SECTION_1_28),
    7300: {
        formula: cumulativeCodeValidationFormula(7300, [7310, 7320, 7330, 7350, 7360, 7390], [7380]),
        description: '7310+7320+7330+7350+7360-7380+7390',
    },
    7400: commonCumulativeCodeValidation(7400, SECTION_1_30),
    7600: commonCumulativeCodeValidation(7600, SECTION_1_21),
    7700: commonCumulativeCodeValidation(7700, SECTION_1_32),
    7800: commonCumulativeCodeValidation(7800, SECTION_1_33),
    8000: {
        formula: cumulativeCodeValidationFormula(8000, [8010, 8020, 8025, 8030, 8040, 8050, 8060, 8080, 8090, 8095, 8100, 8105, 8170], [8110, 8120, 8130, 8140, 8150, 8160, 8180, 8190]),
        description: '8010+8020+8025+8030+8040+8050+8060+8080+8090+8095+8100+8105-8110-8120-8130-8140-8150-8160+8170-8180-8190',
    },
    8200: commonCumulativeCodeValidation(8200, SECTION_1_37),
    8300: commonCumulativeCodeValidation(8300, SECTION_1_38),
    8400: commonCumulativeCodeValidation(8400, SECTION_1_39),
    8500: commonCumulativeCodeValidation(8500, SECTION_1_40),
    8600: commonCumulativeCodeValidation(8600, SECTION_1_41),
    8700: commonCumulativeCodeValidation(8700, SECTION_1_36),
    8888: {
        formula: cumulativeCodeValidationFormula(8888, [7000, 8000, 8700, 8200, 8300, 8400, 8500, 8600]),
        description: '7000+8000+8700+8200+8300+8400+8500+8600',
    },
    9000: {
        formula: cumulativeCodeValidationFormula(9000, [9100, 9200, 9400, 9500]),
        description: '9100+9200+9400+9500',
    },
    9100: commonCumulativeCodeValidation(9100, SECTION_1_43),
    9200: commonCumulativeCodeValidation(9200, SECTION_1_44),
    9400: commonCumulativeCodeValidation(9400, SECTION_1_45),
    9500: commonCumulativeCodeValidation(9500, SECTION_1_46),
    9600: commonCumulativeCodeValidation(9600, SECTION_1_48),
    9700: {
        formula: cumulativeCodeValidationFormula(9700, [9710, 9790], [9720]),
        description: '9710-9720+9790',
    },
    9800: commonCumulativeCodeValidation(9800, SECTION_1_50),
    9900: commonCumulativeCodeValidation(9900, SECTION_1_51),
    9999: {
        formula: cumulativeCodeValidationFormula(9999, [9000, 9600, 9700, 9800, 9900]),
        description: '9000+9600+9700+9800+9900',
    },
};
// Zod schema for the array of balance sheet records
export const balanceSheetArraySchema = z
    .array(balanceSheetRecordSchema)
    .max(150)
    .superRefine((records, ctx) => {
    // Check for unique code values & create a map for faster lookups
    const codeToAmountMap = new Map();
    records.map((record, i) => {
        const code = record.code;
        if (codeToAmountMap.has(code)) {
            ctx.addIssue({
                code: z.ZodIssueCode.custom,
                message: `Duplicate code: ${codeToName(code)}`,
                path: [i, 'code'],
            });
        }
        else {
            codeToAmountMap.set(code, record.amount);
        }
    });
    // Validate each summary field with its formula
    for (const [codeStr, validation] of Object.entries(summaryValidations)) {
        const code = parseInt(codeStr);
        if (codeToAmountMap.has(code)) {
            const actualAmount = codeToAmountMap.get(code);
            const expectedAmount = validation.formula(codeToAmountMap);
            if (actualAmount !== expectedAmount) {
                const index = records.findIndex(r => r.code === code);
                ctx.addIssue({
                    code: z.ZodIssueCode.custom,
                    message: `Value for code ${codeToName(code)} should equal the sum: ${validation.description}`,
                    path: [index, 'amount'],
                });
            }
        }
    }
    // Check total assets equals total liabilities + equity
    const totalAssets = codeToAmountMap.get(8888);
    const totalLiabilitiesEquity = codeToAmountMap.get(9999);
    if (totalAssets !== totalLiabilitiesEquity) {
        ctx.addIssue({
            code: z.ZodIssueCode.custom,
            message: `Total assets (8888) must equal total liabilities + equity (9999). Got: ${totalAssets} vs ${totalLiabilitiesEquity}`,
        });
    }
    // Check if all required summary codes are present
    const requiredSummaryCodes = [7000, 8888, 9000, 9999];
    for (const code of requiredSummaryCodes) {
        if (!codeToAmountMap.has(code)) {
            ctx.addIssue({
                code: z.ZodIssueCode.custom,
                message: `Missing required summary code: ${code}`,
            });
        }
    }
});
//# sourceMappingURL=balance-sheet.js.map