/**
 * Comprehensive tests for src/managers/license-manager.js
 *
 * This is the revenue validation path — an untested bug here could silently
 * break activation for every paying customer (DC-083 priority #1).
 *
 * Unlike license-tier-enforcement.test.js (which stubs _validateOffline and
 * only tests the isPro/tier-gating surface), these tests exercise the REAL
 * crypto flow end-to-end: generateCode(TEST_SECRET, ...) → activate(code) →
 * _validateOffline(code) → verifyCode(secret, code) → credential store.
 *
 * Coverage matrix:
 *   - constructor + load(): credential-store primary, config-backup fallback,
 *     no-license, credential-store error → config recovery, re-store
 *   - activate(): real-code round-trip (30/90/180/365), already-activated
 *     idempotency, invalid format, missing code, offline-validation failure,
 *     LIFETIME rejection (prod) + acceptance (dev env), credential-store
 *     save failure, config write
 *   - activate() online path: server success, server unreachable → offline
 *     fallback, server explicit rejection (no fallback)
 *   - deactivate(): success, no-active-license, credential delete
 *   - getStatus(): free tier, active premium, expired, lifetime
 *   - hasFeature(): no-activation, active, expired, specific-feature
 *   - isPro() / isExpired() / daysRemaining(): all branches
 *   - getMachineFingerprint(): stable, hex format
 *   - requirePremium() middleware: feature-available (next), feature-unavailable (403)
 *   - loadSecret(): file-exists, file-missing, file-unreadable
 *   - _validateOffline(): with-secret, no-secret structural-only, invalid-code,
 *     unsupported-version (forged v2 payload)
 *   - _updateConfig(): writes license + backup, clears on deactivation
 *   - _maskCode(): standard, short, empty
 *   - Full lifecycle: activate → status → deactivate, load-after-activate restore
 */

'use strict';

const fs = require('fs');
const path = require('path');
const os = require('os');
const crypto = require('crypto');

// Use the real keygen to generate cryptographically valid codes.
const { generateCode, verifyCode } = require('../license-keygen');

const TEST_SECRET = 'a'.repeat(64); // 32 bytes hex — deterministic test secret

function _tmpDir() {
  return fs.mkdtempSync(path.join(os.tmpdir(), 'dc-lm-test-'));
}

function _cleanup(dir) {
  try { fs.rmSync(dir, { recursive: true, force: true }); } catch (_) { /* best effort */ }
}

/**
 * Build a LicenseManager with a fresh module instance + env override.
 * Returns { mgr, restore, dir }.
 *
 * @param {Object} opts
 * @param {Object} opts.creds    — credential store stub {store, retrieve, delete}
 * @param {Object} opts.env      — process.env overrides (e.g. LICENSE_SERVER_URL)
 * @param {string} opts.secret   — master secret to load via loadSecret()
 */
function _makeManager(opts = {}) {
  const { creds = {}, env = {}, secret = null } = opts;
  const prevEnv = { ...process.env };
  // Object.assign copies undefined-valued keys as the string "undefined".
  // Delete env keys whose value is undefined so the production "unset" path
  // is exercised accurately.
  for (const [k, v] of Object.entries(env)) {
    if (v === undefined) {
      delete process.env[k];
    } else {
      process.env[k] = v;
    }
  }

  // Force re-require so LICENSE_SERVER_URL is re-read.
  delete require.cache[require.resolve('../src/managers/license-manager')];
  const { LicenseManager } = require('../src/managers/license-manager');

  const dir = _tmpDir();
  const configFile = path.join(dir, 'config.json');
  const secretFile = path.join(dir, '.license-secret');

  const defaultCreds = {
    _store: {},
    async store(key, val) { this._store[key] = val; },
    async retrieve(key) { return this._store[key] || null; },
    async delete(key) { delete this._store[key]; },
  };

  const mgr = new LicenseManager(
    creds._impl ? creds : defaultCreds,
    configFile,
    { info: () => {}, warn: () => {}, error: () => {}, debug: () => {} }
  );

  if (secret) {
    fs.writeFileSync(secretFile, secret, 'utf8');
    mgr.loadSecret(secretFile);
  }

  const restore = async () => {
    process.env = prevEnv;
    _cleanup(dir);
  };

  return { mgr, restore, dir, configFile, secretFile };
}

// ── generateCode helper: mint a real valid code for a given duration ──────

function _mintCode(secret, durationDays, codeId = 1) {
  return generateCode(secret, durationDays, codeId);
}

// ===========================================================================
// constructor + load()
// ===========================================================================

describe('LicenseManager: load()', () => {
  test('loads active license from credential store', async () => {
    const { mgr, restore } = _makeManager({ secret: TEST_SECRET });
    try {
      // Pre-populate the credential store with a valid activation
      const code = _mintCode(TEST_SECRET, 30, 1);
      const activation = {
        code,
        codeId: 1,
        durationDays: 30,
        lifetime: false,
        activatedAt: new Date().toISOString(),
        expiresAt: new Date(Date.now() + 30 * 86400000).toISOString(),
        machineId: 'test',
        validationMethod: 'offline',
        features: ['sso', 'recipes', 'swarm'],
      };
      await mgr.credentialManager.store('license.activation', JSON.stringify(activation));

      await mgr.load();
      expect(mgr.activation).toBeTruthy();
      expect(mgr.activation.code).toBe(code);
      expect(mgr._loaded).toBe(true);
      expect(mgr.isPro()).toBe(true);
    } finally { await restore(); }
  });

  test('logs expired license on load but keeps it', async () => {
    const { mgr, restore } = _makeManager();
    try {
      const activation = {
        code: 'DC-TEST-EXPIRED',
        durationDays: 30,
        lifetime: false,
        activatedAt: '2020-01-01T00:00:00.000Z',
        expiresAt: '2020-02-01T00:00:00.000Z',
        machineId: 'test',
        validationMethod: 'offline',
        features: ['sso'],
      };
      await mgr.credentialManager.store('license.activation', JSON.stringify(activation));

      await mgr.load();
      expect(mgr.activation).toBeTruthy();
      expect(mgr.isExpired()).toBe(true);
      expect(mgr._loaded).toBe(true);
    } finally { await restore(); }
  });

  test('falls back to config.json licenseBackup when credential store fails', async () => {
    const dir = _tmpDir();
    try {
      const configFile = path.join(dir, 'config.json');
      const activation = {
        code: 'DC-BACKUP-TEST',
        durationDays: 90,
        lifetime: false,
        activatedAt: new Date().toISOString(),
        expiresAt: new Date(Date.now() + 90 * 86400000).toISOString(),
        machineId: 'test',
        validationMethod: 'offline',
        features: ['sso'],
      };
      fs.writeFileSync(configFile, JSON.stringify({ licenseBackup: activation }, null, 2));

      // Credential store that throws on retrieve
      const failCreds = {
        async retrieve() { throw new Error('encryption key changed'); },
        async store() {},
        async delete() {},
      };

      delete require.cache[require.resolve('../src/managers/license-manager')];
      const { LicenseManager } = require('../src/managers/license-manager');
      const mgr = new LicenseManager(failCreds, configFile, { info: () => {}, warn: () => {} });

      await mgr.load();
      expect(mgr.activation).toBeTruthy();
      expect(mgr.activation.code).toBe('DC-BACKUP-TEST');
      expect(mgr._loaded).toBe(true);
    } finally { _cleanup(dir); }
  });

  test('re-stores recovered license in credential manager after config backup restore', async () => {
    const dir = _tmpDir();
    try {
      const configFile = path.join(dir, 'config.json');
      const activation = {
        code: 'DC-RESTORE-TEST',
        durationDays: 30,
        lifetime: false,
        activatedAt: new Date().toISOString(),
        expiresAt: new Date(Date.now() + 30 * 86400000).toISOString(),
        machineId: 'test',
        validationMethod: 'offline',
        features: ['sso'],
      };
      fs.writeFileSync(configFile, JSON.stringify({ licenseBackup: activation }, null, 2));

      const storeCalls = [];
      const failCreds = {
        async retrieve() { throw new Error('decryption failed'); },
        async store(key, val) { storeCalls.push({ key, val }); },
        async delete() {},
      };

      delete require.cache[require.resolve('../src/managers/license-manager')];
      const { LicenseManager } = require('../src/managers/license-manager');
      const mgr = new LicenseManager(failCreds, configFile, { info: () => {}, warn: () => {} });

      await mgr.load();
      expect(storeCalls.length).toBe(1);
      expect(storeCalls[0].key).toBe('license.activation');
      expect(JSON.parse(storeCalls[0].val).code).toBe('DC-RESTORE-TEST');
    } finally { _cleanup(dir); }
  });

  test('sets activation=null when no license anywhere', async () => {
    const { mgr, restore } = _makeManager();
    try {
      await mgr.load();
      expect(mgr.activation).toBeNull();
      expect(mgr._loaded).toBe(true);
    } finally { await restore(); }
  });

  test('sets activation=null when config.json has no licenseBackup', async () => {
    const dir = _tmpDir();
    try {
      const configFile = path.join(dir, 'config.json');
      fs.writeFileSync(configFile, JSON.stringify({ someOtherField: true }, null, 2));

      const emptyCreds = {
        async retrieve() { return null; },
        async store() {},
        async delete() {},
      };

      delete require.cache[require.resolve('../src/managers/license-manager')];
      const { LicenseManager } = require('../src/managers/license-manager');
      const mgr = new LicenseManager(emptyCreds, configFile, { info: () => {} });

      await mgr.load();
      expect(mgr.activation).toBeNull();
    } finally { _cleanup(dir); }
  });
});

// ===========================================================================
// activate() — the core revenue path
// ===========================================================================

describe('LicenseManager: activate() — real crypto round-trip', () => {
  test('activates a valid 30-day code via offline HMAC validation', async () => {
    const { mgr, restore } = _makeManager({ secret: TEST_SECRET });
    try {
      const code = _mintCode(TEST_SECRET, 30, 100);
      const result = await mgr.activate(code);

      expect(result.success).toBe(true);
      expect(result.message).toMatch(/30 days/);
      expect(result.activation).toBeTruthy();
      expect(result.activation.active).toBe(true);
      expect(result.activation.tier).toBe('premium');
      expect(result.activation.durationDays).toBe(30);
      expect(result.activation.lifetime).toBe(false);
      expect(result.activation.daysRemaining).toBeGreaterThan(29);
      expect(result.activation.daysRemaining).toBeLessThanOrEqual(30);
    } finally { await restore(); }
  });

  test('activates valid 90, 180, and 365-day codes', async () => {
    for (const duration of [90, 180, 365]) {
      const { mgr, restore } = _makeManager({ secret: TEST_SECRET });
      try {
        const code = _mintCode(TEST_SECRET, duration, 200 + duration);
        const result = await mgr.activate(code);
        expect(result.success).toBe(true);
        expect(result.activation.durationDays).toBe(duration);
        expect(result.activation.daysRemaining).toBeGreaterThan(duration - 1);
      } finally { await restore(); }
    }
  });

  test('persists activation to credential store after successful activation', async () => {
    const { mgr, restore } = _makeManager({ secret: TEST_SECRET });
    try {
      const code = _mintCode(TEST_SECRET, 30, 300);
      await mgr.activate(code);

      const stored = await mgr.credentialManager.retrieve('license.activation');
      expect(stored).toBeTruthy();
      const parsed = JSON.parse(stored);
      expect(parsed.code).toBe(code);
      expect(parsed.durationDays).toBe(30);
    } finally { await restore(); }
  });

  test('writes config.json with license info + backup after activation', async () => {
    const { mgr, restore, configFile } = _makeManager({ secret: TEST_SECRET });
    try {
      const code = _mintCode(TEST_SECRET, 90, 400);
      await mgr.activate(code);

      const config = JSON.parse(fs.readFileSync(configFile, 'utf8'));
      expect(config.license.active).toBe(true);
      expect(config.license.tier).toBe('premium');
      expect(config.licenseBackup).toBeTruthy();
      expect(config.licenseBackup.code).toBe(code);
    } finally { await restore(); }
  });

  test('idempotent: activating the same valid code twice returns already-activated', async () => {
    const { mgr, restore } = _makeManager({ secret: TEST_SECRET });
    try {
      const code = _mintCode(TEST_SECRET, 30, 500);
      const result1 = await mgr.activate(code);
      expect(result1.success).toBe(true);

      const result2 = await mgr.activate(code);
      expect(result2.success).toBe(true);
      expect(result2.message).toMatch(/already activated/i);
    } finally { await restore(); }
  });

  test('rejects empty/null/undefined code', async () => {
    const { mgr, restore } = _makeManager({ secret: TEST_SECRET });
    try {
      expect((await mgr.activate('')).success).toBe(false);
      expect((await mgr.activate(null)).success).toBe(false);
      expect((await mgr.activate(undefined)).success).toBe(false);
      expect((await mgr.activate(12345)).success).toBe(false);
    } finally { await restore(); }
  });

  test('rejects code without DC- prefix', async () => {
    const { mgr, restore } = _makeManager({ secret: TEST_SECRET });
    try {
      const result = await mgr.activate('XYZ-ABCDE-FGHIJ-KLMNO-PQRST-UVWXY');
      expect(result.success).toBe(false);
      expect(result.message).toMatch(/DC-/);
    } finally { await restore(); }
  });

  test('rejects code with invalid HMAC signature (all-A base32, forged)', async () => {
    const { mgr, restore } = _makeManager({ secret: TEST_SECRET });
    try {
      const result = await mgr.activate('DC-AAAAA-AAAAA-AAAAA-AAAAA-AAAAA');
      // Structurally valid base32 (all 'A') parses fine but the HMAC computed
      // from the decoded payload won't match — offline validation fails.
      expect(result.success).toBe(false);
    } finally { await restore(); }
  });

  test('rejects code with wrong HMAC signature (forged)', async () => {
    const { mgr, restore } = _makeManager({ secret: TEST_SECRET });
    try {
      // Generate with a DIFFERENT secret, try to activate with TEST_SECRET
      const wrongSecret = 'b'.repeat(64);
      const forgedCode = _mintCode(wrongSecret, 30, 600);
      const result = await mgr.activate(forgedCode);
      expect(result.success).toBe(false);
      expect(result.message).toMatch(/invalid|forged|corrupted/i);
    } finally { await restore(); }
  });

  test('LIFETIME code is rejected in production (ALLOW_LIFETIME_LICENSE unset)', async () => {
    const { mgr, restore } = _makeManager({
      secret: TEST_SECRET,
      env: { ALLOW_LIFETIME_LICENSE: undefined },
    });
    try {
      const lifetimeCode = _mintCode(TEST_SECRET, 0, 700); // durationDays=0 → lifetime
      const result = await mgr.activate(lifetimeCode);
      expect(result.success).toBe(false);
      expect(result.message).toMatch(/lifetime/i);
      expect(result.message).toMatch(/not available/i);
      expect(mgr.activation).toBeNull();
    } finally { await restore(); }
  });

  test('LIFETIME code is accepted when ALLOW_LIFETIME_LICENSE=true', async () => {
    const { mgr, restore } = _makeManager({
      secret: TEST_SECRET,
      env: { ALLOW_LIFETIME_LICENSE: 'true' },
    });
    try {
      const lifetimeCode = _mintCode(TEST_SECRET, 0, 800);
      const result = await mgr.activate(lifetimeCode);
      expect(result.success).toBe(true);
      expect(result.activation.lifetime).toBe(true);
      expect(result.activation.tier).toBe('premium');
      expect(result.activation.expiresAt).toBeNull();
      expect(result.activation.daysRemaining).toBeNull();
      expect(mgr.isPro()).toBe(true);
    } finally { await restore(); }
  });

  test('returns failure when credential store throws on save', async () => {
    const failCreds = {
      async store() { throw new Error('disk full'); },
      async retrieve() { return null; },
      async delete() {},
    };
    const { mgr, restore } = _makeManager({
      secret: TEST_SECRET,
      creds: { _impl: true, ...failCreds },
    });
    try {
      const code = _mintCode(TEST_SECRET, 30, 900);
      const result = await mgr.activate(code);
      expect(result.success).toBe(false);
      expect(result.message).toMatch(/failed to save/i);
    } finally { await restore(); }
  });

  test('normalizes lowercase code to uppercase', async () => {
    const { mgr, restore } = _makeManager({ secret: TEST_SECRET });
    try {
      const code = _mintCode(TEST_SECRET, 30, 1000);
      const result = await mgr.activate(code.toLowerCase());
      expect(result.success).toBe(true);
      expect(mgr.activation.code).toBe(code); // stored uppercase
    } finally { await restore(); }
  });

  test('trims whitespace around code', async () => {
    const { mgr, restore } = _makeManager({ secret: TEST_SECRET });
    try {
      const code = _mintCode(TEST_SECRET, 30, 1100);
      const result = await mgr.activate(`  ${code}  `);
      expect(result.success).toBe(true);
    } finally { await restore(); }
  });
});

// ===========================================================================
// activate() — online validation path
//
// LICENSE_SERVER_URL is a module-level const read at require() time, so we
// use jest.isolateModules to get a fresh module instance with the env var
// set. The credential manager stub is created inside the isolation callback.
// ===========================================================================

describe('LicenseManager: activate() — online validation', () => {
  test('uses online server when LICENSE_SERVER_URL is set and returns success', async () => {
    const originalFetch = global.fetch;
    const dir = _tmpDir();
    const prevUrl = process.env.LICENSE_SERVER_URL;
    process.env.LICENSE_SERVER_URL = 'https://license.test.example';
    try {
      global.fetch = jest.fn().mockResolvedValue({
        ok: true,
        json: async () => ({
          success: true,
          codeId: 999,
          durationDays: 365,
          expiresAt: new Date(Date.now() + 365 * 86400000).toISOString(),
          features: ['sso', 'recipes', 'swarm'],
          token: 'srv-token-abc',
        }),
      });

      const creds = {
        _store: {},
        async store(key, val) { this._store[key] = val; },
        async retrieve(key) { return this._store[key] || null; },
        async delete(key) { delete this._store[key]; },
      };

      let result;
      jest.isolateModules(() => {
        const { LicenseManager } = require('../src/managers/license-manager');
        const mgr = new LicenseManager(creds, path.join(dir, 'config.json'), { info: () => {}, warn: () => {}, error: () => {} });
        // Run synchronously — activate is async but we capture the promise
        result = mgr.activate('DC-ABCDE-FGHIJ-KLMNO-PQRST-UVWXY');
      });

      const res = await result;
      expect(res.success).toBe(true);
      expect(res.activation.validationMethod).toBe('online');
      expect(global.fetch).toHaveBeenCalledWith(
        'https://license.test.example/api/license/validate',
        expect.objectContaining({ method: 'POST' })
      );
    } finally {
      if (prevUrl === undefined) delete process.env.LICENSE_SERVER_URL;
      else process.env.LICENSE_SERVER_URL = prevUrl;
      global.fetch = originalFetch;
      _cleanup(dir);
    }
  });

  test('falls back to offline when server is unreachable (fetch throws)', async () => {
    const originalFetch = global.fetch;
    const dir = _tmpDir();
    const prevUrl = process.env.LICENSE_SERVER_URL;
    process.env.LICENSE_SERVER_URL = 'https://license.test.example';
    try {
      global.fetch = jest.fn().mockRejectedValue(new Error('ECONNREFUSED'));

      const secretFile = path.join(dir, '.license-secret');
      fs.writeFileSync(secretFile, TEST_SECRET, 'utf8');

      const creds = {
        _store: {},
        async store(key, val) { this._store[key] = val; },
        async retrieve(key) { return this._store[key] || null; },
        async delete(key) { delete this._store[key]; },
      };

      let result;
      jest.isolateModules(() => {
        const { LicenseManager } = require('../src/managers/license-manager');
        const mgr = new LicenseManager(creds, path.join(dir, 'config.json'), { info: () => {}, warn: () => {}, error: () => {} });
        mgr.loadSecret(secretFile);
        const code = _mintCode(TEST_SECRET, 30, 1200);
        result = mgr.activate(code);
      });

      const res = await result;
      expect(res.success).toBe(true);
      expect(res.activation.validationMethod).toBe('offline');
    } finally {
      if (prevUrl === undefined) delete process.env.LICENSE_SERVER_URL;
      else process.env.LICENSE_SERVER_URL = prevUrl;
      global.fetch = originalFetch;
      _cleanup(dir);
    }
  });

  test('rejects when server explicitly returns failure (no fallback)', async () => {
    const originalFetch = global.fetch;
    const dir = _tmpDir();
    const prevUrl = process.env.LICENSE_SERVER_URL;
    process.env.LICENSE_SERVER_URL = 'https://license.test.example';
    try {
      global.fetch = jest.fn().mockResolvedValue({
        ok: false,
        status: 403,
        json: async () => ({ error: 'Code revoked by administrator' }),
      });

      const creds = {
        _store: {},
        async store(key, val) { this._store[key] = val; },
        async retrieve(key) { return this._store[key] || null; },
        async delete(key) { delete this._store[key]; },
      };

      let result;
      jest.isolateModules(() => {
        const { LicenseManager } = require('../src/managers/license-manager');
        const mgr = new LicenseManager(creds, path.join(dir, 'config.json'), { info: () => {}, warn: () => {}, error: () => {} });
        // Use a code with correct length (25 base32 chars = 5 groups of 5)
        result = mgr.activate('DC-ABCDE-FGHIJ-KLMNO-PQRST-UVWXY');
      });

      const res = await result;
      expect(res.success).toBe(false);
      expect(res.message).toMatch(/revoked/i);
    } finally {
      if (prevUrl === undefined) delete process.env.LICENSE_SERVER_URL;
      else process.env.LICENSE_SERVER_URL = prevUrl;
      global.fetch = originalFetch;
      _cleanup(dir);
    }
  });
});

// ===========================================================================
// deactivate()
// ===========================================================================

describe('LicenseManager: deactivate()', () => {
  test('deactivates an active license', async () => {
    const { mgr, restore } = _makeManager({ secret: TEST_SECRET });
    try {
      const code = _mintCode(TEST_SECRET, 30, 1300);
      await mgr.activate(code);
      expect(mgr.isPro()).toBe(true);

      const result = await mgr.deactivate();
      expect(result.success).toBe(true);
      expect(result.message).toMatch(/deactivated/i);
      expect(mgr.activation).toBeNull();
      expect(mgr.isPro()).toBe(false);
    } finally { await restore(); }
  });

  test('clears credential store on deactivation', async () => {
    const { mgr, restore } = _makeManager({ secret: TEST_SECRET });
    try {
      const code = _mintCode(TEST_SECRET, 30, 1400);
      await mgr.activate(code);
      expect(await mgr.credentialManager.retrieve('license.activation')).toBeTruthy();

      await mgr.deactivate();
      expect(await mgr.credentialManager.retrieve('license.activation')).toBeNull();
    } finally { await restore(); }
  });

  test('updates config.json to free tier after deactivation', async () => {
    const { mgr, restore, configFile } = _makeManager({ secret: TEST_SECRET });
    try {
      const code = _mintCode(TEST_SECRET, 30, 1500);
      await mgr.activate(code);

      // Verify config has license
      let config = JSON.parse(fs.readFileSync(configFile, 'utf8'));
      expect(config.license.active).toBe(true);

      await mgr.deactivate();

      config = JSON.parse(fs.readFileSync(configFile, 'utf8'));
      expect(config.license.active).toBe(false);
      expect(config.license.tier).toBe('free');
      expect(config.licenseBackup).toBeUndefined();
    } finally { await restore(); }
  });

  test('returns failure when no active license', async () => {
    const { mgr, restore } = _makeManager();
    try {
      const result = await mgr.deactivate();
      expect(result.success).toBe(false);
      expect(result.message).toMatch(/no active/i);
    } finally { await restore(); }
  });
});

// ===========================================================================
// getStatus()
// ===========================================================================

describe('LicenseManager: getStatus()', () => {
  test('returns free tier when no activation', () => {
    const { mgr, restore } = _makeManager();
    try {
      const status = mgr.getStatus();
      expect(status.active).toBe(false);
      expect(status.tier).toBe('free');
      expect(status.features).toEqual([]);
      expect(status.premiumFeatures).toBeTruthy();
    } finally { restore(); }
  });

  test('returns premium tier for active license', () => {
    const { mgr, restore } = _makeManager({ secret: TEST_SECRET });
    try {
      mgr.activation = {
        code: 'DC-STATUS-TEST',
        durationDays: 30,
        lifetime: false,
        activatedAt: new Date().toISOString(),
        expiresAt: new Date(Date.now() + 30 * 86400000).toISOString(),
        machineId: 'test',
        validationMethod: 'offline',
        features: ['sso'],
      };
      const status = mgr.getStatus();
      expect(status.active).toBe(true);
      expect(status.tier).toBe('premium');
      expect(status.features).toContain('sso');
      expect(status.daysRemaining).toBeGreaterThan(29);
    } finally { restore(); }
  });

  test('returns free tier for expired license', () => {
    const { mgr, restore } = _makeManager();
    try {
      mgr.activation = {
        code: 'DC-EXPIRED-STATUS',
        durationDays: 30,
        lifetime: false,
        activatedAt: '2020-01-01T00:00:00Z',
        expiresAt: '2020-02-01T00:00:00Z',
        machineId: 'test',
        validationMethod: 'offline',
        features: ['sso'],
      };
      const status = mgr.getStatus();
      expect(status.active).toBe(false);
      expect(status.tier).toBe('free');
      expect(status.expired).toBe(true);
      expect(status.features).toEqual([]);
    } finally { restore(); }
  });

  test('returns null expiresAt/daysRemaining for lifetime', () => {
    const { mgr, restore } = _makeManager();
    try {
      mgr.activation = {
        code: 'DC-LIFETIME-STATUS',
        durationDays: 0,
        lifetime: true,
        activatedAt: new Date().toISOString(),
        expiresAt: new Date('2099-12-31T23:59:59.999Z').toISOString(),
        machineId: 'test',
        validationMethod: 'offline',
        features: ['sso', 'recipes'],
      };
      const status = mgr.getStatus();
      expect(status.active).toBe(true);
      expect(status.lifetime).toBe(true);
      expect(status.expiresAt).toBeNull();
      expect(status.daysRemaining).toBeNull();
    } finally { restore(); }
  });

  test('masks the code in status output', () => {
    const { mgr, restore } = _makeManager();
    try {
      mgr.activation = {
        code: 'DC-ABCDE-FGHIJ-KLMNO-PQRST-UVWXY',
        durationDays: 30,
        lifetime: false,
        activatedAt: new Date().toISOString(),
        expiresAt: new Date(Date.now() + 30 * 86400000).toISOString(),
        machineId: 'test',
        validationMethod: 'offline',
        features: ['sso'],
      };
      const status = mgr.getStatus();
      expect(status.code).not.toBe(mgr.activation.code);
      expect(status.code).toMatch(/^DC-/);
      expect(status.code).toContain('*****');
    } finally { restore(); }
  });
});

// ===========================================================================
// hasFeature()
// ===========================================================================

describe('LicenseManager: hasFeature()', () => {
  test('returns false when no activation', () => {
    const { mgr, restore } = _makeManager();
    try {
      expect(mgr.hasFeature('sso')).toBe(false);
    } finally { restore(); }
  });

  test('returns true for available feature on active license', () => {
    const { mgr, restore } = _makeManager();
    try {
      mgr.activation = {
        code: 'DC-FEATURE-TEST',
        durationDays: 90,
        lifetime: false,
        activatedAt: new Date().toISOString(),
        expiresAt: new Date(Date.now() + 90 * 86400000).toISOString(),
        machineId: 'test',
        validationMethod: 'offline',
        features: ['sso', 'recipes'],
      };
      expect(mgr.hasFeature('sso')).toBe(true);
      expect(mgr.hasFeature('recipes')).toBe(true);
    } finally { restore(); }
  });

  test('returns false for unavailable feature', () => {
    const { mgr, restore } = _makeManager();
    try {
      mgr.activation = {
        code: 'DC-FEATURE-TEST',
        durationDays: 90,
        lifetime: false,
        activatedAt: new Date().toISOString(),
        expiresAt: new Date(Date.now() + 90 * 86400000).toISOString(),
        machineId: 'test',
        validationMethod: 'offline',
        features: ['sso'],
      };
      expect(mgr.hasFeature('swarm')).toBe(false);
    } finally { restore(); }
  });

  test('returns false when license expired', () => {
    const { mgr, restore } = _makeManager();
    try {
      mgr.activation = {
        code: 'DC-EXPIRED-FEATURE',
        durationDays: 30,
        lifetime: false,
        activatedAt: '2020-01-01T00:00:00Z',
        expiresAt: '2020-02-01T00:00:00Z',
        machineId: 'test',
        validationMethod: 'offline',
        features: ['sso'],
      };
      expect(mgr.hasFeature('sso')).toBe(false);
    } finally { restore(); }
  });

  test('falls back to PREMIUM_FEATURES keys when activation.features missing', () => {
    const { mgr, restore } = _makeManager();
    try {
      mgr.activation = {
        code: 'DC-NO-FEATURES-LIST',
        durationDays: 30,
        lifetime: false,
        activatedAt: new Date().toISOString(),
        expiresAt: new Date(Date.now() + 30 * 86400000).toISOString(),
        machineId: 'test',
        validationMethod: 'offline',
        // features omitted
      };
      // Should default to all PREMIUM_FEATURES
      expect(mgr.hasFeature('sso')).toBe(true);
      expect(mgr.hasFeature('recipes')).toBe(true);
      expect(mgr.hasFeature('swarm')).toBe(true);
    } finally { restore(); }
  });
});

// ===========================================================================
// isPro() / isExpired() / daysRemaining()
// ===========================================================================

describe('LicenseManager: isPro()', () => {
  test('false when no activation', () => {
    const { mgr, restore } = _makeManager();
    try {
      expect(mgr.isPro()).toBe(false);
    } finally { restore(); }
  });

  test('true for active non-lifetime license', () => {
    const { mgr, restore } = _makeManager();
    try {
      mgr.activation = {
        code: 'DC-PRO-TEST',
        durationDays: 30,
        lifetime: false,
        activatedAt: new Date().toISOString(),
        expiresAt: new Date(Date.now() + 30 * 86400000).toISOString(),
        machineId: 'test',
        validationMethod: 'offline',
        features: ['sso'],
      };
      expect(mgr.isPro()).toBe(true);
    } finally { restore(); }
  });

  test('true for active lifetime license', () => {
    const { mgr, restore } = _makeManager();
    try {
      mgr.activation = {
        code: 'DC-LIFETIME-PRO',
        durationDays: 0,
        lifetime: true,
        activatedAt: new Date().toISOString(),
        expiresAt: new Date('2099-12-31').toISOString(),
        machineId: 'test',
        validationMethod: 'offline',
        features: ['sso'],
      };
      expect(mgr.isPro()).toBe(true);
    } finally { restore(); }
  });

  test('false for expired license', () => {
    const { mgr, restore } = _makeManager();
    try {
      mgr.activation = {
        code: 'DC-EXPIRED-PRO',
        durationDays: 30,
        lifetime: false,
        activatedAt: '2020-01-01T00:00:00Z',
        expiresAt: '2020-02-01T00:00:00Z',
        machineId: 'test',
        validationMethod: 'offline',
        features: ['sso'],
      };
      expect(mgr.isPro()).toBe(false);
    } finally { restore(); }
  });
});

describe('LicenseManager: isExpired()', () => {
  test('true when no activation', () => {
    const { mgr, restore } = _makeManager();
    try {
      expect(mgr.isExpired()).toBe(true);
    } finally { restore(); }
  });

  test('false for lifetime (durationDays=0)', () => {
    const { mgr, restore } = _makeManager();
    try {
      mgr.activation = { durationDays: 0, lifetime: true, expiresAt: null };
      expect(mgr.isExpired()).toBe(false);
    } finally { restore(); }
  });

  test('false for lifetime flag only (no durationDays)', () => {
    const { mgr, restore } = _makeManager();
    try {
      mgr.activation = { lifetime: true, expiresAt: '2020-01-01T00:00:00Z' };
      expect(mgr.isExpired()).toBe(false);
    } finally { restore(); }
  });

  test('false when expiresAt is null/missing (treated as lifetime)', () => {
    const { mgr, restore } = _makeManager();
    try {
      mgr.activation = { durationDays: 30, lifetime: false, expiresAt: null };
      expect(mgr.isExpired()).toBe(false);
    } finally { restore(); }
  });

  test('true when expiry is in the past', () => {
    const { mgr, restore } = _makeManager();
    try {
      mgr.activation = {
        durationDays: 30,
        lifetime: false,
        expiresAt: new Date(Date.now() - 86400000).toISOString(),
      };
      expect(mgr.isExpired()).toBe(true);
    } finally { restore(); }
  });

  test('false when expiry is in the future', () => {
    const { mgr, restore } = _makeManager();
    try {
      mgr.activation = {
        durationDays: 30,
        lifetime: false,
        expiresAt: new Date(Date.now() + 86400000).toISOString(),
      };
      expect(mgr.isExpired()).toBe(false);
    } finally { restore(); }
  });
});

describe('LicenseManager: daysRemaining()', () => {
  test('returns 0 when no activation', () => {
    const { mgr, restore } = _makeManager();
    try {
      expect(mgr.daysRemaining()).toBe(0);
    } finally { restore(); }
  });

  test('returns positive days for active license', () => {
    const { mgr, restore } = _makeManager();
    try {
      mgr.activation = {
        expiresAt: new Date(Date.now() + 15 * 86400000).toISOString(),
      };
      const days = mgr.daysRemaining();
      expect(days).toBeGreaterThanOrEqual(14);
      expect(days).toBeLessThanOrEqual(15);
    } finally { restore(); }
  });

  test('returns negative for expired license (Math.ceil of negative)', () => {
    const { mgr, restore } = _makeManager();
    try {
      mgr.activation = {
        expiresAt: new Date(Date.now() - 5 * 86400000).toISOString(),
      };
      const days = mgr.daysRemaining();
      expect(days).toBeLessThanOrEqual(-4);
    } finally { restore(); }
  });
});

// ===========================================================================
// getMachineFingerprint()
// ===========================================================================

describe('LicenseManager: getMachineFingerprint()', () => {
  test('returns a 16-char hex string', () => {
    const { mgr, restore } = _makeManager();
    try {
      const fp = mgr.getMachineFingerprint();
      expect(fp).toMatch(/^[0-9a-f]{16}$/);
    } finally { restore(); }
  });

  test('is stable across calls (same machine)', () => {
    const { mgr, restore } = _makeManager();
    try {
      const fp1 = mgr.getMachineFingerprint();
      const fp2 = mgr.getMachineFingerprint();
      expect(fp1).toBe(fp2);
    } finally { restore(); }
  });
});

// ===========================================================================
// requirePremium() middleware
// ===========================================================================

describe('LicenseManager: requirePremium() middleware', () => {
  test('calls next() when feature is available', () => {
    const { mgr, restore } = _makeManager();
    try {
      mgr.activation = {
        code: 'DC-MW-TEST',
        durationDays: 30,
        lifetime: false,
        activatedAt: new Date().toISOString(),
        expiresAt: new Date(Date.now() + 30 * 86400000).toISOString(),
        machineId: 'test',
        validationMethod: 'offline',
        features: ['sso'],
      };

      const middleware = mgr.requirePremium('sso');
      const req = {};
      const res = {};
      let nextCalled = false;
      middleware(req, res, (err) => { nextCalled = !err; });
      expect(nextCalled).toBe(true);
    } finally { restore(); }
  });

  test('returns 403 with premiumRequired when feature unavailable', () => {
    const { mgr, restore } = _makeManager();
    try {
      const middleware = mgr.requirePremium('sso');
      const req = {};
      let status = null;
      let body = null;
      const res = {
        status(s) { status = s; return this; },
        json(b) { body = b; return this; },
      };
      middleware(req, res, () => {});
      expect(status).toBe(403);
      expect(body.success).toBe(false);
      expect(body.premiumRequired).toBe(true);
      expect(body.feature).toBe('sso');
    } finally { restore(); }
  });

  test('includes upgrade URL in 403 response', () => {
    const { mgr, restore } = _makeManager();
    try {
      const middleware = mgr.requirePremium('recipes');
      const req = {};
      let body = null;
      const res = {
        status() { return this; },
        json(b) { body = b; return this; },
      };
      middleware(req, res, () => {});
      expect(body.upgradeUrl).toMatch(/settings.*license|license.*settings/i);
    } finally { restore(); }
  });

  test('handles unknown feature key gracefully', () => {
    const { mgr, restore } = _makeManager();
    try {
      const middleware = mgr.requirePremium('nonexistent');
      const req = {};
      let body = null;
      const res = {
        status() { return this; },
        json(b) { body = b; return this; },
      };
      middleware(req, res, () => {});
      expect(body.featureName).toBe('nonexistent');
    } finally { restore(); }
  });
});

// ===========================================================================
// loadSecret()
// ===========================================================================

describe('LicenseManager: loadSecret()', () => {
  test('loads secret from file and returns true', () => {
    const dir = _tmpDir();
    try {
      const secretFile = path.join(dir, '.license-secret');
      fs.writeFileSync(secretFile, 'testsecret123', 'utf8');

      delete require.cache[require.resolve('../src/managers/license-manager')];
      const { LicenseManager } = require('../src/managers/license-manager');
      const mgr = new LicenseManager({}, '/nonexistent', { info: () => {} });

      const result = mgr.loadSecret(secretFile);
      expect(result).toBe(true);
      expect(mgr.masterSecretHash).toBe('testsecret123');
    } finally { _cleanup(dir); }
  });

  test('returns false when secret file does not exist', () => {
    const { mgr, restore } = _makeManager();
    try {
      const result = mgr.loadSecret('/nonexistent/secret/path');
      expect(result).toBe(false);
      expect(mgr.masterSecretHash).toBeNull();
    } finally { restore(); }
  });

  test('returns false and logs warning when secret file read throws', () => {
    const dir = _tmpDir();
    // Mock fs.existsSync + fs.readFileSync; restore in finally so any
    // failure doesn't poison the rest of the suite.
    const origExists = fs.existsSync;
    const origRead = fs.readFileSync;
    let warnCalled = false;
    try {
      fs.existsSync = () => true;
      fs.readFileSync = () => { throw new Error('EACCES: permission denied'); };

      delete require.cache[require.resolve('../src/managers/license-manager')];
      const { LicenseManager } = require('../src/managers/license-manager');
      const mgr = new LicenseManager({}, '/nonexistent', { info: () => {}, warn: () => { warnCalled = true; } });

      const result = mgr.loadSecret('/fake/secret/path');
      expect(result).toBe(false);
      expect(mgr.masterSecretHash).toBeNull();
      expect(warnCalled).toBe(true);
    } finally {
      fs.existsSync = origExists;
      fs.readFileSync = origRead;
      _cleanup(dir);
    }
  });
});

// ===========================================================================
// _validateOffline()
// ===========================================================================

describe('LicenseManager: _validateOffline()', () => {
  test('validates real code with loaded secret', () => {
    const { mgr, restore } = _makeManager({ secret: TEST_SECRET });
    try {
      const code = _mintCode(TEST_SECRET, 30, 1600);
      const result = mgr._validateOffline(code);
      expect(result.valid).toBe(true);
      expect(result.durationDays).toBe(30);
      expect(result.codeId).toBe(1600);
    } finally { restore(); }
  });

  test('rejects forged code (HMAC mismatch)', () => {
    const { mgr, restore } = _makeManager({ secret: TEST_SECRET });
    try {
      const wrongSecret = crypto.randomBytes(32).toString('hex');
      const forgedCode = _mintCode(wrongSecret, 30, 1700);
      const result = mgr._validateOffline(forgedCode);
      expect(result.valid).toBe(false);
      expect(result.reason).toMatch(/signature|forged|corrupted/i);
    } finally { restore(); }
  });

  test('returns validation-unavailable when no secret loaded', () => {
    const { mgr, restore } = _makeManager({ secret: null });
    try {
      const code = _mintCode(TEST_SECRET, 30, 1800);
      const result = mgr._validateOffline(code);
      expect(result.valid).toBe(false);
      expect(result.reason).toMatch(/unavailable|internet/i);
    } finally { restore(); }
  });

  test('returns invalid for malformed code string', () => {
    const { mgr, restore } = _makeManager({ secret: null });
    try {
      const result = mgr._validateOffline('DC-GARBAGE');
      expect(result.valid).toBe(false);
    } finally { restore(); }
  });

  test('rejects code with unsupported version (forged version-2 payload)', () => {
    // Construct a code whose version nibble is 2 (not the current VERSION=1).
    // We can't use generateCode() because it hardcodes VERSION=1, so we
    // manually pack a version=2 payload, sign it with the correct HMAC, and
    // base32-encode it into the DC-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX format.
    const { generateCode, parseCode } = require('../license-keygen');
    // Generate a valid version-1 code, decode it, bump the version nibble,
    // re-sign, re-encode. This gives a cryptographically well-formed code
    // with version=2 — the structural check should catch it.
    const realCode = generateCode(TEST_SECRET, 30, 1900);
    // Decode payload + signature from the real code
    const cleaned = realCode.replace(/^DC-/, '').replace(/-/g, '');
    const BASE32 = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
    function b32Decode(str) {
      let bits = '';
      for (const ch of str.toUpperCase()) { bits += BASE32.indexOf(ch).toString(2).padStart(5, '0'); }
      const bytes = [];
      for (let i = 0; i + 8 <= bits.length; i += 8) bytes.push(parseInt(bits.substring(i, i + 8), 2));
      return Buffer.from(bytes);
    }
    const decoded = b32Decode(cleaned);
    const payload = Buffer.from(decoded.subarray(0, 10));
    // Overwrite version nibble: bits 12-15 of the first 16-bit value.
    const versionAndDuration = payload.readUInt16BE(0);
    const duration = versionAndDuration & 0x0FFF;
    payload.writeUInt16BE((2 << 12) | duration, 0); // version=2
    // Re-sign with the same secret so HMAC is valid for the tampered payload
    const hmac = crypto.createHmac('sha256', TEST_SECRET).update(payload).digest();
    const signature = hmac.subarray(0, 5);
    const combined = Buffer.concat([payload, signature]);
    // Re-encode base32
    function b32Encode(buf) {
      let bits = '';
      for (const b of buf) bits += b.toString(2).padStart(8, '0');
      while (bits.length % 5 !== 0) bits += '0';
      let result = '';
      for (let i = 0; i < bits.length; i += 5) result += BASE32[parseInt(bits.substring(i, i + 5), 2)];
      return result;
    }
    let encoded = b32Encode(combined);
    while (encoded.length < 25) encoded += '0';
    encoded = encoded.substring(0, 25);
    const groups = [];
    for (let i = 0; i < 25; i += 5) groups.push(encoded.substring(i, i + 5));
    const forgedV2Code = `DC-${groups.join('-')}`;

    // Verify the forged code actually parses as version=2
    const parsed = parseCode(forgedV2Code);
    expect(parsed.version).toBe(2);

    const { mgr, restore } = _makeManager({ secret: TEST_SECRET });
    try {
      const result = mgr._validateOffline(forgedV2Code);
      expect(result.valid).toBe(false);
      expect(result.reason).toMatch(/version/i);
    } finally { restore(); }
  });
});

// ===========================================================================
// _maskCode()
// ===========================================================================

describe('LicenseManager: _maskCode()', () => {
  test('masks a standard code (DC + 5 groups)', () => {
    const { mgr, restore } = _makeManager();
    try {
      const masked = mgr._maskCode('DC-ABCDE-FGHIJ-KLMNO-PQRST-UVWXY');
      expect(masked).toBe('DC-ABCDE-*****-*****-UVWXY');
    } finally { restore(); }
  });

  test('returns DC-***** for short code (< 4 groups)', () => {
    const { mgr, restore } = _makeManager();
    try {
      expect(mgr._maskCode('DC-ABC')).toBe('DC-*****');
      expect(mgr._maskCode('DC-ABC-DEF')).toBe('DC-*****');
    } finally { restore(); }
  });

  test('returns "none" for null/empty', () => {
    const { mgr, restore } = _makeManager();
    try {
      expect(mgr._maskCode(null)).toBe('none');
      expect(mgr._maskCode('')).toBe('none');
      expect(mgr._maskCode(undefined)).toBe('none');
    } finally { restore(); }
  });
});

// ===========================================================================
// allowsLifetimeLicense()
// ===========================================================================

describe('LicenseManager: allowsLifetimeLicense()', () => {
  test('defaults to false', () => {
    const { mgr, restore } = _makeManager();
    try {
      expect(mgr.allowsLifetimeLicense()).toBe(false);
    } finally { restore(); }
  });

  test('returns true when ALLOW_LIFETIME_LICENSE=true', () => {
    const { mgr, restore } = _makeManager({
      env: { ALLOW_LIFETIME_LICENSE: 'true' },
    });
    try {
      expect(mgr.allowsLifetimeLicense()).toBe(true);
    } finally { restore(); }
  });

  test('returns false for non-"true" values', () => {
    const { mgr, restore } = _makeManager({
      env: { ALLOW_LIFETIME_LICENSE: 'false' },
    });
    try {
      expect(mgr.allowsLifetimeLicense()).toBe(false);
    } finally { restore(); }
  });
});

// ===========================================================================
// _updateConfig() — internal but critical for persistence
// ===========================================================================

describe('LicenseManager: _updateConfig()', () => {
  test('creates config.json if it does not exist', async () => {
    const { mgr, restore, configFile } = _makeManager();
    try {
      expect(fs.existsSync(configFile)).toBe(false);

      mgr.activation = {
        code: 'DC-CREATE-CONFIG',
        durationDays: 30,
        lifetime: false,
        activatedAt: new Date().toISOString(),
        expiresAt: new Date(Date.now() + 30 * 86400000).toISOString(),
        machineId: 'test',
        validationMethod: 'offline',
        features: ['sso'],
      };

      await mgr._updateConfig();
      expect(fs.existsSync(configFile)).toBe(true);
      const config = JSON.parse(fs.readFileSync(configFile, 'utf8'));
      expect(config.license.active).toBe(true);
      expect(config.licenseBackup.code).toBe('DC-CREATE-CONFIG');
    } finally { restore(); }
  });

  test('preserves existing config fields when updating', async () => {
    const { mgr, restore, configFile } = _makeManager();
    try {
      fs.writeFileSync(configFile, JSON.stringify({
        tld: '.sami',
        existingField: 'preserved',
      }, null, 2));

      mgr.activation = {
        code: 'DC-PRESERVE-CONFIG',
        durationDays: 30,
        lifetime: false,
        activatedAt: new Date().toISOString(),
        expiresAt: new Date(Date.now() + 30 * 86400000).toISOString(),
        machineId: 'test',
        validationMethod: 'offline',
        features: ['sso'],
      };

      await mgr._updateConfig();
      const config = JSON.parse(fs.readFileSync(configFile, 'utf8'));
      expect(config.tld).toBe('.sami');
      expect(config.existingField).toBe('preserved');
      expect(config.license.active).toBe(true);
    } finally { restore(); }
  });

  test('clears licenseBackup when activation is null/expired', async () => {
    const { mgr, restore, configFile } = _makeManager();
    try {
      fs.writeFileSync(configFile, JSON.stringify({
        license: { active: true, tier: 'premium' },
        licenseBackup: { code: 'DC-OLD', durationDays: 30 },
      }, null, 2));

      mgr.activation = null;
      await mgr._updateConfig();
      const config = JSON.parse(fs.readFileSync(configFile, 'utf8'));
      expect(config.license.active).toBe(false);
      expect(config.license.tier).toBe('free');
      expect(config.licenseBackup).toBeUndefined();
    } finally { restore(); }
  });

  test('does not crash when config file directory does not exist', async () => {
    const { mgr, restore } = _makeManager();
    try {
      // Point to a path inside a nonexistent directory
      mgr.configFile = '/nonexistent/dir/config.json';
      mgr.activation = {
        code: 'DC-NO-CRASH',
        durationDays: 30,
        lifetime: false,
        activatedAt: new Date().toISOString(),
        expiresAt: new Date(Date.now() + 30 * 86400000).toISOString(),
        machineId: 'test',
        features: ['sso'],
      };
      // Should not throw — _updateConfig catches internally
      await expect(mgr._updateConfig()).resolves.not.toThrow();
    } finally { restore(); }
  });
});

// ===========================================================================
// Full lifecycle integration
// ===========================================================================

describe('LicenseManager: full lifecycle integration', () => {
  test('activate → getStatus → deactivate → getStatus', async () => {
    const { mgr, restore } = _makeManager({ secret: TEST_SECRET });
    try {
      // Initially free
      expect(mgr.getStatus().tier).toBe('free');

      // Activate
      const code = _mintCode(TEST_SECRET, 90, 2000);
      const activateResult = await mgr.activate(code);
      expect(activateResult.success).toBe(true);

      // Check status is premium
      const activeStatus = mgr.getStatus();
      expect(activeStatus.active).toBe(true);
      expect(activeStatus.tier).toBe('premium');
      expect(activeStatus.durationDays).toBe(90);

      // Deactivate
      const deactivateResult = await mgr.deactivate();
      expect(deactivateResult.success).toBe(true);

      // Back to free
      expect(mgr.getStatus().tier).toBe('free');
      expect(mgr.isPro()).toBe(false);
    } finally { await restore(); }
  });

  test('load() after activate() restores the same activation', async () => {
    const creds = {
      _store: {},
      async store(key, val) { this._store[key] = val; },
      async retrieve(key) { return this._store[key] || null; },
      async delete(key) { delete this._store[key]; },
    };
    const dir = _tmpDir();
    try {
      const configFile = path.join(dir, 'config.json');
      const secretFile = path.join(dir, '.license-secret');
      fs.writeFileSync(secretFile, TEST_SECRET, 'utf8');

      delete require.cache[require.resolve('../src/managers/license-manager')];
      const { LicenseManager } = require('../src/managers/license-manager');

      // Activate
      const mgr1 = new LicenseManager(creds, configFile, { info: () => {} });
      mgr1.loadSecret(secretFile);
      const code = _mintCode(TEST_SECRET, 30, 2100);
      await mgr1.activate(code);
      const originalCodeId = mgr1.activation.codeId;

      // Simulate restart: create a NEW manager with same creds + config
      const mgr2 = new LicenseManager(creds, configFile, { info: () => {} });
      await mgr2.load();

      expect(mgr2.activation).toBeTruthy();
      expect(mgr2.activation.code).toBe(code);
      expect(mgr2.activation.codeId).toBe(originalCodeId);
      expect(mgr2.isPro()).toBe(true);
    } finally { _cleanup(dir); }
  });

  test('freshly minted code validates as active (not expired)', async () => {
    const { mgr, restore } = _makeManager({ secret: TEST_SECRET });
    try {
      // Generate a code and verify it independently
      const code = _mintCode(TEST_SECRET, 30, 2200);
      const verifyResult = verifyCode(TEST_SECRET, code);
      expect(verifyResult.valid).toBe(true);
      expect(verifyResult.durationDays).toBe(30);
      expect(verifyResult.expired).toBe(false);

      // Activate should succeed (code is cryptographically valid + not expired)
      const result = await mgr.activate(code);
      expect(result.success).toBe(true);
      expect(result.activation.expired).toBe(false);
    } finally { await restore(); }
  });
});
