// ==========================================
// Channel API Contract Tests
// ==========================================
// Validates channel listing, type filtering, and
// the sidebar's data flow. Catches the channel_type
// vs type parameter mismatch (v0.37.14.2 fix).
//
// Run: node --test src/js/__tests__/channel-contracts.test.js
// ==========================================

const { describe, it } = require('node:test');
const assert = require('node:assert/strict');

// ── Channel list response shape ──────────────

describe('GET /channels response contract', () => {
    // Backend response from ListChannels handler:
    //   c.JSON(200, gin.H{"data": channels, "total": total, ...})
    const backendResponse = {
        data: [
            { id: 'c1', title: 'General', type: 'channel', folder_id: null },
            { id: 'c2', title: 'AI Chat', type: 'direct', folder_id: 'f1' },
            { id: 'c3', title: 'Dev Group', type: 'group', folder_id: null },
            { id: 'c4', title: 'DM: Alice', type: 'dm', folder_id: null },
        ],
        total: 4,
        page: 1,
        per_page: 100,
    };

    it('response has "data" array', () => {
        assert.ok(Array.isArray(backendResponse.data), 'response.data must be an array');
    });

    it('each channel has required fields', () => {
        for (const ch of backendResponse.data) {
            assert.ok(ch.id, 'channel must have id');
            assert.ok(ch.type, 'channel must have type');
            assert.ok(['direct', 'dm', 'group', 'channel', 'workflow'].includes(ch.type),
                `unknown type: ${ch.type}`);
        }
    });
});

// ── Type filter parameter naming ──────────────

describe('Channel list type filter', () => {
    // Simulates URL parameter building (like _qs in api-domains.js)
    function buildQueryString(opts) {
        const p = new URLSearchParams();
        for (const [k, v] of Object.entries(opts)) {
            if (v != null) p.set(k, String(v));
        }
        const s = p.toString();
        return s ? '?' + s : '';
    }

    it('should use "type" param (not "channel_type")', () => {
        // The Go handler expects "type" or "types"
        const qs = buildQueryString({ page: 1, per_page: 100, type: 'direct' });
        assert.ok(qs.includes('type=direct'), `query should contain type=direct, got: ${qs}`);
        assert.ok(!qs.includes('channel_type'), 'should NOT use channel_type param');
    });

    it('should use "types" for multi-type filter', () => {
        const qs = buildQueryString({ page: 1, per_page: 200, types: 'direct,dm,group,channel' });
        assert.ok(qs.includes('types=direct'), `query should contain types param, got: ${qs}`);
    });

    it('single type results should all match requested type', () => {
        const channels = [
            { id: 'c1', type: 'direct' },
            { id: 'c2', type: 'direct' },
        ];
        for (const ch of channels) {
            assert.equal(ch.type, 'direct', `type filter leaked: ${ch.type}`);
        }
    });

    it('multi-type results should only contain requested types', () => {
        const channels = [
            { id: 'c1', type: 'direct' },
            { id: 'c2', type: 'group' },
            { id: 'c3', type: 'channel' },
            { id: 'c4', type: 'dm' },
        ];
        const allowed = new Set(['direct', 'dm', 'group', 'channel']);
        for (const ch of channels) {
            assert.ok(allowed.has(ch.type), `unexpected type: ${ch.type}`);
        }
    });
});

// ── Sidebar merge deduplication ───────────────

describe('Sidebar channel merge', () => {
    it('single request should not produce duplicates', () => {
        // After fix: sidebar uses a single request with types=direct,dm,group,channel
        const apiResponse = [
            { id: 'c1', title: 'Chat 1', type: 'direct' },
            { id: 'c2', title: 'General', type: 'channel' },
            { id: 'c3', title: 'DM: Bob', type: 'dm' },
        ];
        const ids = apiResponse.map(ch => ch.id);
        const uniqueIds = new Set(ids);
        assert.equal(ids.length, uniqueIds.size, 'no duplicate ids expected');
    });

    it('_extract handles nested .data or flat array', () => {
        // Mirrors use-sidebar.js _extract pattern
        function extract(resp) {
            if (!resp) return [];
            if (Array.isArray(resp)) return resp;
            if (resp.data && Array.isArray(resp.data)) return resp.data;
            return [];
        }

        assert.deepEqual(extract({ data: [{ id: '1' }] }), [{ id: '1' }]);
        assert.deepEqual(extract([{ id: '2' }]), [{ id: '2' }]);
        assert.deepEqual(extract(null), []);
        assert.deepEqual(extract({}), []);
    });
});

// ── Channel type feature matrix ───────────────

describe('Channel type feature matrix', () => {
    const AI_TYPES = new Set(['direct', 'workflow']);

    it('direct type shows model selector', () => {
        assert.ok(AI_TYPES.has('direct'));
    });

    it('workflow type shows model selector', () => {
        assert.ok(AI_TYPES.has('workflow'));
    });

    it('dm type hides model selector', () => {
        assert.ok(!AI_TYPES.has('dm'));
    });

    it('group type hides model selector', () => {
        assert.ok(!AI_TYPES.has('group'));
    });

    it('channel type hides model selector', () => {
        assert.ok(!AI_TYPES.has('channel'));
    });
});
