import test from 'node:test';
import assert from 'node:assert/strict';
import { createImmersionTrackerStartupHandler } from './immersion-startup';

function makeConfig() {
  return {
    immersionTracking: {
      enabled: true,
      batchSize: 40,
      flushIntervalMs: 1500,
      queueCap: 500,
      payloadCapBytes: 16000,
      maintenanceIntervalMs: 3600000,
      retention: {
        eventsDays: 14,
        telemetryDays: 30,
        sessionsDays: 45,
        dailyRollupsDays: 180,
        monthlyRollupsDays: 730,
        vacuumIntervalDays: 7,
      },
    },
  };
}

test('createImmersionTrackerStartupHandler skips when disabled', () => {
  const calls: string[] = [];
  let tracker: unknown = 'unchanged';
  const handler = createImmersionTrackerStartupHandler({
    getResolvedConfig: () => ({
      immersionTracking: {
        ...makeConfig().immersionTracking,
        enabled: false,
      },
    }),
    getConfiguredDbPath: () => '/tmp/subminer.db',
    createTrackerService: () => {
      calls.push('createTrackerService');
      return {};
    },
    setTracker: (nextTracker) => {
      tracker = nextTracker;
    },
    getMpvClient: () => null,
    seedTrackerFromCurrentMedia: () => calls.push('seedTracker'),
    logInfo: (message) => calls.push(`info:${message}`),
    logDebug: (message) => calls.push(`debug:${message}`),
    logWarn: (message) => calls.push(`warn:${message}`),
  });

  handler();

  assert.ok(calls.includes('info:Immersion tracking disabled in config'));
  assert.equal(calls.includes('createTrackerService'), false);
  assert.equal(calls.includes('seedTracker'), false);
  assert.equal(tracker, 'unchanged');
});

test('createImmersionTrackerStartupHandler skips when env disables session tracking', () => {
  const calls: string[] = [];
  const originalEnv = process.env.SUBMINER_DISABLE_IMMERSION_TRACKING;
  process.env.SUBMINER_DISABLE_IMMERSION_TRACKING = '1';

  try {
    let tracker: unknown = 'unchanged';
    const handler = createImmersionTrackerStartupHandler({
      getResolvedConfig: () => {
        calls.push('getResolvedConfig');
        return makeConfig();
      },
      getConfiguredDbPath: () => {
        calls.push('getConfiguredDbPath');
        return '/tmp/subminer.db';
      },
      createTrackerService: () => {
        calls.push('createTrackerService');
        return {};
      },
      setTracker: (nextTracker) => {
        tracker = nextTracker;
      },
      getMpvClient: () => null,
      seedTrackerFromCurrentMedia: () => calls.push('seedTracker'),
      logInfo: (message) => calls.push(`info:${message}`),
      logDebug: (message) => calls.push(`debug:${message}`),
      logWarn: (message) => calls.push(`warn:${message}`),
    });

    handler();

    assert.equal(calls.includes('getResolvedConfig'), false);
    assert.equal(calls.includes('getConfiguredDbPath'), false);
    assert.equal(calls.includes('createTrackerService'), false);
    assert.equal(calls.includes('seedTracker'), false);
    assert.equal(tracker, 'unchanged');
    assert.ok(
      calls.includes(
        'info:Immersion tracking disabled for this session by SUBMINER_DISABLE_IMMERSION_TRACKING=1.',
      ),
    );
  } finally {
    if (originalEnv === undefined) {
      delete process.env.SUBMINER_DISABLE_IMMERSION_TRACKING;
    } else {
      process.env.SUBMINER_DISABLE_IMMERSION_TRACKING = originalEnv;
    }
  }
});

test('createImmersionTrackerStartupHandler creates tracker and auto-connects mpv', () => {
  const calls: string[] = [];
  const trackerInstance = { kind: 'tracker' };
  let assignedTracker: unknown = null;
  let receivedDbPath = '';
  let receivedPolicy: unknown;
  let connectCalls = 0;
  const handler = createImmersionTrackerStartupHandler({
    getResolvedConfig: () => makeConfig(),
    getConfiguredDbPath: () => '/tmp/subminer.db',
    createTrackerService: (params) => {
      receivedDbPath = params.dbPath;
      receivedPolicy = params.policy;
      return trackerInstance;
    },
    setTracker: (nextTracker) => {
      assignedTracker = nextTracker;
    },
    getMpvClient: () => ({
      connected: false,
      connect: () => {
        connectCalls += 1;
      },
    }),
    seedTrackerFromCurrentMedia: () => calls.push('seedTracker'),
    logInfo: (message) => calls.push(`info:${message}`),
    logDebug: (message) => calls.push(`debug:${message}`),
    logWarn: (message) => calls.push(`warn:${message}`),
  });

  handler();

  assert.equal(receivedDbPath, '/tmp/subminer.db');
  assert.deepEqual(receivedPolicy, {
    batchSize: 40,
    flushIntervalMs: 1500,
    queueCap: 500,
    payloadCapBytes: 16000,
    maintenanceIntervalMs: 3600000,
    retention: {
      eventsDays: 14,
      telemetryDays: 30,
      sessionsDays: 45,
      dailyRollupsDays: 180,
      monthlyRollupsDays: 730,
      vacuumIntervalDays: 7,
    },
  });
  assert.equal(assignedTracker, trackerInstance);
  assert.equal(connectCalls, 1);
  assert.ok(calls.includes('seedTracker'));
  assert.ok(calls.includes('info:Auto-connecting MPV client for immersion tracking'));
});

test('createImmersionTrackerStartupHandler keeps tracker startup alive when mpv auto-connect throws', () => {
  const calls: string[] = [];
  const trackerInstance = { kind: 'tracker' };
  let assignedTracker: unknown = null;
  const handler = createImmersionTrackerStartupHandler({
    getResolvedConfig: () => makeConfig(),
    getConfiguredDbPath: () => '/tmp/subminer.db',
    createTrackerService: () => trackerInstance,
    setTracker: (nextTracker) => {
      assignedTracker = nextTracker;
    },
    getMpvClient: () => ({
      connected: false,
      connect: () => {
        throw new Error('socket not ready');
      },
    }),
    seedTrackerFromCurrentMedia: () => calls.push('seedTracker'),
    logInfo: (message) => calls.push(`info:${message}`),
    logDebug: (message) => calls.push(`debug:${message}`),
    logWarn: (message, details) => calls.push(`warn:${message}:${(details as Error).message}`),
  });

  handler();

  assert.equal(assignedTracker, trackerInstance);
  assert.ok(calls.includes('seedTracker'));
  assert.ok(
    calls.includes(
      'warn:MPV auto-connect failed during immersion tracker startup; continuing.:socket not ready',
    ),
  );
  assert.equal(
    calls.some((entry) =>
      entry.startsWith('warn:Immersion tracker startup failed; disabling tracking.'),
    ),
    false,
  );
});

test('createImmersionTrackerStartupHandler disables tracker on failure', () => {
  const calls: string[] = [];
  let assignedTracker: unknown = 'initial';
  const handler = createImmersionTrackerStartupHandler({
    getResolvedConfig: () => makeConfig(),
    getConfiguredDbPath: () => '/tmp/subminer.db',
    createTrackerService: () => {
      throw new Error('db unavailable');
    },
    setTracker: (nextTracker) => {
      assignedTracker = nextTracker;
    },
    getMpvClient: () => null,
    seedTrackerFromCurrentMedia: () => calls.push('seedTracker'),
    logInfo: (message) => calls.push(`info:${message}`),
    logDebug: (message) => calls.push(`debug:${message}`),
    logWarn: (message, details) => calls.push(`warn:${message}:${(details as Error).message}`),
  });

  handler();

  assert.equal(assignedTracker, null);
  assert.equal(calls.includes('seedTracker'), false);
  assert.ok(
    calls.includes('warn:Immersion tracker startup failed; disabling tracking.:db unavailable'),
  );
});

test('createImmersionTrackerStartupHandler skips mpv auto-connect when disabled by caller', () => {
  let connectCalls = 0;
  const handler = createImmersionTrackerStartupHandler({
    getResolvedConfig: () => makeConfig(),
    getConfiguredDbPath: () => '/tmp/subminer.db',
    createTrackerService: () => ({}),
    setTracker: () => {},
    getMpvClient: () => ({
      connected: false,
      connect: () => {
        connectCalls += 1;
      },
    }),
    shouldAutoConnectMpv: () => false,
    seedTrackerFromCurrentMedia: () => {},
    logInfo: () => {},
    logDebug: () => {},
    logWarn: () => {},
  });

  handler();

  assert.equal(connectCalls, 0);
});
