/**
 * TDD for the errorClass → vendor-layer attribution map + the per-layer
 * downtime breakdown aggregator.
 */
import {
    ERROR_CLASS_TO_LAYER,
    layerForErrorClass,
    attributeDowntimeByLayer,
    VENDOR_LAYERS,
} from '../error-class-layer';
import type { ErrorClass } from '@/lib/monitor-strategies';

describe('ERROR_CLASS_TO_LAYER', () => {
    it('maps each documented errorClass to the spec layer', () => {
        expect(layerForErrorClass('DNS_FAIL')).toBe('DNS');
        expect(layerForErrorClass('TLS_INVALID')).toBe('SSL');
        expect(layerForErrorClass('TCP_REJECT')).toBe('HOST');
        expect(layerForErrorClass('TIMEOUT')).toBe('ISP');
        expect(layerForErrorClass('HTTP_5XX')).toBe('APP');
        expect(layerForErrorClass('HTTP_4XX')).toBe('APP');
        expect(layerForErrorClass('KEYWORD_MISS')).toBe('APP');
    });

    it('treats UNKNOWN (and null) as unattributed', () => {
        expect(layerForErrorClass('UNKNOWN')).toBeNull();
        expect(layerForErrorClass(null)).toBeNull();
        expect(layerForErrorClass(undefined)).toBeNull();
    });

    it('covers every ErrorClass value (no silent gap)', () => {
        const all: ErrorClass[] = [
            'DNS_FAIL', 'TCP_REJECT', 'TLS_INVALID', 'HTTP_5XX',
            'HTTP_4XX', 'TIMEOUT', 'KEYWORD_MISS', 'UNKNOWN',
        ];
        for (const ec of all) {
            // Must be present in the map (value may be null for UNKNOWN).
            expect(Object.prototype.hasOwnProperty.call(ERROR_CLASS_TO_LAYER, ec)).toBe(true);
        }
    });
});

describe('attributeDowntimeByLayer', () => {
    it('returns all-zero with empty input', () => {
        const r = attributeDowntimeByLayer([]);
        for (const layer of VENDOR_LAYERS) {
            expect(r.byLayer[layer]).toBe(0);
        }
        expect(r.unattributed).toBe(0);
        expect(r.totalDownSamples).toBe(0);
    });

    it('counts down samples per layer and tracks percentages', () => {
        const r = attributeDowntimeByLayer([
            { errorClass: 'TIMEOUT', count: 6 },   // ISP
            { errorClass: 'DNS_FAIL', count: 2 },  // DNS
            { errorClass: 'HTTP_5XX', count: 2 },  // APP
        ]);
        expect(r.byLayer.ISP).toBe(6);
        expect(r.byLayer.DNS).toBe(2);
        expect(r.byLayer.APP).toBe(2);
        expect(r.byLayer.SSL).toBe(0);
        expect(r.totalDownSamples).toBe(10);
        expect(r.percentByLayer.ISP).toBe(60);
        expect(r.percentByLayer.DNS).toBe(20);
        expect(r.percentByLayer.APP).toBe(20);
    });

    it('routes UNKNOWN to unattributed, not a layer', () => {
        const r = attributeDowntimeByLayer([
            { errorClass: 'UNKNOWN', count: 3 },
            { errorClass: 'TCP_REJECT', count: 1 }, // HOST
        ]);
        expect(r.unattributed).toBe(3);
        expect(r.byLayer.HOST).toBe(1);
        expect(r.totalDownSamples).toBe(4);
        expect(r.percentByLayer.HOST).toBe(25);
        expect(r.percentUnattributed).toBe(75);
    });

    it('aggregates repeated error classes', () => {
        const r = attributeDowntimeByLayer([
            { errorClass: 'TIMEOUT', count: 2 },
            { errorClass: 'TIMEOUT', count: 3 },
        ]);
        expect(r.byLayer.ISP).toBe(5);
    });
});
