/**
 * ICD Test Runner — CRUD: Notifications
 * Full preference lifecycle + notification endpoint error paths.
 */
(function () {
  'use strict';
  var T = window.ICD;
  if (!T) return;
  if (!T.crud) T.crud = {};

  T.crud.notifications = async function (testTag) {

    // ── Preference: Set ──
    await T.test('crud', 'notifications', 'PUT /notifications/preferences/:type (set)', async function () {
      var d = await T.apiPut('/notifications/preferences/kb.ready', {
        in_app: true, email: true
      });
      T.assert(typeof d === 'object', 'expected object');
      T.assert(d.type === 'kb.ready', 'type should be kb.ready, got: ' + d.type);
      T.assert(d.in_app === true, 'in_app should be true');
      T.assert(d.email === true, 'email should be true');
    });

    // ── Preference: Read back via list ──
    await T.test('crud', 'notifications', 'GET /notifications/preferences (round-trip)', async function () {
      var d = await T.apiGet('/notifications/preferences');
      T.assertHasKey(d, 'data', 'preferences envelope');
      T.assert(Array.isArray(d.data), 'data should be array');
      var found = d.data.find(function (p) { return p.type === 'kb.ready'; });
      T.assert(found, 'kb.ready preference should be in list');
      T.assert(found.in_app === true, 'in_app should be true');
      T.assert(found.email === true, 'email should be true');
    });

    // ── Preference: Partial update ──
    await T.test('crud', 'notifications', 'PUT /notifications/preferences/:type (partial update)', async function () {
      var d = await T.apiPut('/notifications/preferences/kb.ready', {
        email: false
      });
      T.assert(d.in_app === true, 'in_app should remain true after partial update');
      T.assert(d.email === false, 'email should be false after partial update');
    });

    // ── Preference: Wildcard ──
    await T.test('crud', 'notifications', 'PUT /notifications/preferences/* (wildcard)', async function () {
      var d = await T.apiPut('/notifications/preferences/*', {
        in_app: false, email: true
      });
      T.assert(d.type === '*', 'type should be *, got: ' + d.type);
      T.assert(d.in_app === false, 'in_app should be false');
      T.assert(d.email === true, 'email should be true');
    });

    // ── Preference: Delete + verify removed ──
    await T.test('crud', 'notifications', 'DELETE /notifications/preferences/:type (reset)', async function () {
      await T.safeDelete('/notifications/preferences/kb.ready');
      var d = await T.apiGet('/notifications/preferences');
      var found = (d.data || []).find(function (p) { return p.type === 'kb.ready'; });
      T.assert(!found, 'kb.ready should be removed after delete');
    });

    // ── Preference: Idempotent delete ──
    await T.test('crud', 'notifications', 'DELETE /notifications/preferences/:type (idempotent)', async function () {
      await T.safeDelete('/notifications/preferences/nonexistent.type');
    });

    // ── Cleanup wildcard preference ──
    await T.test('crud', 'notifications', 'DELETE /notifications/preferences/* (cleanup)', async function () {
      await T.safeDelete('/notifications/preferences/*');
    });

    // ── Notification list: envelope shape ──
    await T.test('crud', 'notifications', 'GET /notifications (envelope shape)', async function () {
      var d = await T.apiGet('/notifications');
      T.assertHasKey(d, 'data', 'notifications envelope');
      T.assertHasKey(d, 'total', 'notifications total');
      T.assertHasKey(d, 'limit', 'notifications limit');
      T.assertHasKey(d, 'offset', 'notifications offset');
      T.assert(Array.isArray(d.data), 'data should be array');
      T.assert(typeof d.total === 'number', 'total should be number');
      T.assert(typeof d.limit === 'number', 'limit should be number');
      T.assert(typeof d.offset === 'number', 'offset should be number');
    });

    // ── Notification list: pagination params ──
    await T.test('crud', 'notifications', 'GET /notifications?limit=2 (pagination)', async function () {
      var d = await T.apiGet('/notifications?limit=2&offset=0');
      T.assert(d.limit === 2, 'limit should be 2, got: ' + d.limit);
      T.assert(d.offset === 0, 'offset should be 0, got: ' + d.offset);
    });

    // ── Notification list: unread_only filter ──
    await T.test('crud', 'notifications', 'GET /notifications?unread_only=true', async function () {
      var d = await T.apiGet('/notifications?unread_only=true');
      T.assertHasKey(d, 'data', 'unread envelope');
      T.assert(Array.isArray(d.data), 'unread data should be array');
    });

    // ── Unread count: shape ──
    await T.test('crud', 'notifications', 'GET /notifications/unread-count (shape)', async function () {
      var d = await T.apiGet('/notifications/unread-count');
      T.assertHasKey(d, 'count', 'unread-count');
      T.assert(typeof d.count === 'number', 'count should be number');
    });

    // ── Mark all read: no-op on empty ──
    await T.test('crud', 'notifications', 'POST /notifications/mark-all-read (no-op)', async function () {
      var d = await T.apiPost('/notifications/mark-all-read');
      T.assertHasKey(d, 'ok', 'mark-all-read');
      T.assert(d.ok === true, 'ok should be true');
    });

    // ── Mark read: non-existent → 404 ──
    await T.test('crud', 'notifications', 'PATCH /notifications/:id/read (not found → 404)', async function () {
      var token = await T.getAuthToken();
      var d = await T.authFetch(token, 'PATCH', '/notifications/00000000-0000-0000-0000-000000000000/read');
      T.assertStatus(d, 404, 'mark read non-existent');
    });

    // ── Delete: non-existent → 404 ──
    await T.test('crud', 'notifications', 'DELETE /notifications/:id (not found → 404)', async function () {
      var token = await T.getAuthToken();
      var d = await T.authFetch(token, 'DELETE', '/notifications/00000000-0000-0000-0000-000000000000');
      T.assertStatus(d, 404, 'delete non-existent');
    });

    // ── Admin broadcast (v0.28.6) ──

    await T.test('crud', 'notifications', 'POST /admin/notifications/broadcast', async function () {
      var d = await T.apiPost('/admin/notifications/broadcast', {
        title: 'ICD Test Broadcast',
        message: 'Automated test broadcast.',
        level: 'info'
      });
      T.assertHasKey(d, 'message', 'broadcast response');
      T.assertHasKey(d, 'count', 'broadcast response');
      T.assert(d.count >= 1, 'count should be >= 1');
    });

    await T.test('crud', 'notifications', 'POST /admin/notifications/broadcast (missing title → 400)', async function () {
      var token = await T.getAuthToken();
      var d = await T.authFetch(token, 'POST', '/admin/notifications/broadcast', { message: 'no title' });
      T.assertStatus(d, 400, 'missing title');
    });

    await T.test('crud', 'notifications', 'POST /admin/notifications/broadcast (invalid level → 400)', async function () {
      var token = await T.getAuthToken();
      var d = await T.authFetch(token, 'POST', '/admin/notifications/broadcast', { title: 'x', message: 'x', level: 'extreme' });
      T.assertStatus(d, 400, 'invalid level');
    });

  };
})();
