'use strict';

const test = require('node:test');
const assert = require('node:assert/strict');

const AssetResolver = require('../../src/registry/AssetResolver');

function fakeNs(name, entries) {
    const map = new Map(entries.map(([k, v]) => [String(k).toLowerCase(), v]));
    return {
        name,
        loadAll: () => new Map(map),
        refresh: async () => new Map(map),
    };
}

test('resolve() hits the cache on first call and is sync', () => {
    const r = new AssetResolver([fakeNs('curves', [['m1', { foo: 1 }]])]);
    assert.deepEqual(r.resolve('curves', 'm1'), { foo: 1 });
});

test('resolve() is case-insensitive', () => {
    const r = new AssetResolver([fakeNs('curves', [['MyModel', { ok: true }]])]);
    assert.deepEqual(r.resolve('curves', 'mymodel'), { ok: true });
    assert.deepEqual(r.resolve('curves', 'MYMODEL'), { ok: true });
});

test('resolve() returns null for unknown id', () => {
    const r = new AssetResolver([fakeNs('curves', [['m1', { foo: 1 }]])]);
    assert.equal(r.resolve('curves', 'm999'), null);
    assert.equal(r.resolve('curves', ''),     null);
    assert.equal(r.resolve('curves', null),   null);
});

test('resolve() throws on unknown namespace', () => {
    const r = new AssetResolver([fakeNs('curves', [['m1', { foo: 1 }]])]);
    assert.throws(() => r.resolve('nope', 'm1'), /unknown namespace/i);
});

test('list() returns all ids in the namespace', () => {
    const r = new AssetResolver([fakeNs('curves', [['a', 1], ['b', 2]])]);
    assert.deepEqual(r.list('curves').sort(), ['a', 'b']);
});

test('namespaces() lists every registered namespace', () => {
    const r = new AssetResolver([
        fakeNs('curves', []),
        fakeNs('menu', []),
    ]);
    assert.deepEqual(r.namespaces().sort(), ['curves', 'menu']);
});

test('refresh(name) re-hydrates a single namespace', async () => {
    let counter = 0;
    const ns = {
        name: 'curves',
        loadAll: () => new Map([['m1', { v: ++counter }]]),
        refresh: async () => new Map([['m1', { v: ++counter }]]),
    };
    const r = new AssetResolver([ns]);
    assert.deepEqual(r.resolve('curves', 'm1'), { v: 1 });
    await r.refresh('curves');
    assert.deepEqual(r.resolve('curves', 'm1'), { v: 2 });
});

test('refresh() with no name re-hydrates every namespace', async () => {
    let cA = 0, cB = 0;
    const r = new AssetResolver([
        { name: 'a', loadAll: () => new Map([['x', { v: ++cA }]]), refresh: async () => new Map([['x', { v: ++cA }]]) },
        { name: 'b', loadAll: () => new Map([['y', { v: ++cB }]]), refresh: async () => new Map([['y', { v: ++cB }]]) },
    ]);
    r.resolve('a', 'x');
    r.resolve('b', 'y');
    await r.refresh();
    assert.equal(r.resolve('a', 'x').v, 2);
    assert.equal(r.resolve('b', 'y').v, 2);
});

test('constructor rejects malformed namespaces', () => {
    assert.throws(() => new AssetResolver([{ name: 'x' }]),       /loadAll/);
    assert.throws(() => new AssetResolver([{ loadAll: () => {} }]), /name/);
});

test('resolveAssetMetadata walks supplier→type→model and returns derived fields', () => {
    const r = new AssetResolver([{
        name: 'menu',
        loadAll: () => new Map([['rotatingmachine', {
            softwareType: 'rotatingmachine',
            suppliers: [{
                id: 'hidrostal', name: 'Hidrostal',
                types: [{ id: 'pump-centrifugal', name: 'Centrifugal',
                    models: [{ id: 'm1', name: 'M-one', units: ['l/s', 'm3/h'] }],
                }],
            }],
        }]]),
    }]);
    const meta = r.resolveAssetMetadata('rotatingmachine', 'm1');
    assert.equal(meta.supplier, 'Hidrostal');
    assert.equal(meta.type,     'Centrifugal');
    assert.equal(meta.model,    'M-one');
    assert.deepEqual(meta.units, ['l/s', 'm3/h']);
});

test('resolveAssetMetadata returns null on missing model', () => {
    const r = new AssetResolver([{
        name: 'menu',
        loadAll: () => new Map([['rotatingmachine', { suppliers: [] }]]),
    }]);
    assert.equal(r.resolveAssetMetadata('rotatingmachine', 'm-nope'), null);
    assert.equal(r.resolveAssetMetadata('rotatingmachine', null),     null);
    assert.equal(r.resolveAssetMetadata(null,              'm1'),     null);
});
