import assert from 'node:assert/strict';
import test from 'node:test';
import {
  notifyCharacterDictionaryAutoSyncStatus,
  type CharacterDictionaryAutoSyncNotificationEvent,
} from './character-dictionary-auto-sync-notifications';
import { createStartupOsdSequencer } from './startup-osd-sequencer';

function makeEvent(
  phase: CharacterDictionaryAutoSyncNotificationEvent['phase'],
  message: string,
): CharacterDictionaryAutoSyncNotificationEvent {
  return {
    phase,
    mediaId: 101291,
    mediaTitle: 'Rascal Does Not Dream of Bunny Girl Senpai',
    message,
  };
}

test('auto sync notifications send osd updates for progress phases', () => {
  const calls: string[] = [];

  notifyCharacterDictionaryAutoSyncStatus(makeEvent('checking', 'checking'), {
    getNotificationType: () => 'osd',
    showOsd: (message) => {
      calls.push(`osd:${message}`);
    },
    showDesktopNotification: (title, options) =>
      calls.push(`desktop:${title}:${options.body ?? ''}`),
  });
  notifyCharacterDictionaryAutoSyncStatus(makeEvent('generating', 'generating'), {
    getNotificationType: () => 'osd',
    showOsd: (message) => {
      calls.push(`osd:${message}`);
    },
    showDesktopNotification: (title, options) =>
      calls.push(`desktop:${title}:${options.body ?? ''}`),
  });
  notifyCharacterDictionaryAutoSyncStatus(makeEvent('syncing', 'syncing'), {
    getNotificationType: () => 'osd',
    showOsd: (message) => {
      calls.push(`osd:${message}`);
    },
    showDesktopNotification: (title, options) =>
      calls.push(`desktop:${title}:${options.body ?? ''}`),
  });
  notifyCharacterDictionaryAutoSyncStatus(makeEvent('importing', 'importing'), {
    getNotificationType: () => 'osd',
    showOsd: (message) => {
      calls.push(`osd:${message}`);
    },
    showDesktopNotification: (title, options) =>
      calls.push(`desktop:${title}:${options.body ?? ''}`),
  });
  notifyCharacterDictionaryAutoSyncStatus(makeEvent('ready', 'ready'), {
    getNotificationType: () => 'osd',
    showOsd: (message) => {
      calls.push(`osd:${message}`);
    },
    showDesktopNotification: (title, options) =>
      calls.push(`desktop:${title}:${options.body ?? ''}`),
  });

  assert.deepEqual(calls, [
    'osd:checking',
    'osd:generating',
    'osd:syncing',
    'osd:importing',
    'osd:ready',
  ]);
});

test('auto sync notifications send overlay and desktop delivery for both', () => {
  const calls: string[] = [];

  notifyCharacterDictionaryAutoSyncStatus(makeEvent('syncing', 'syncing'), {
    getNotificationType: () => 'both',
    showOsd: (message) => {
      calls.push(`osd:${message}`);
    },
    showDesktopNotification: (title, options) =>
      calls.push(`desktop:${title}:${options.body ?? ''}`),
    showOverlayNotification: (payload) =>
      calls.push(
        `overlay:${payload.id}:${payload.historyId}:${payload.title}:${payload.body}:${payload.persistent ? 'pin' : 'auto'}`,
      ),
  });
  notifyCharacterDictionaryAutoSyncStatus(makeEvent('ready', 'ready'), {
    getNotificationType: () => 'both',
    showOsd: (message) => {
      calls.push(`osd:${message}`);
    },
    showDesktopNotification: (title, options) =>
      calls.push(`desktop:${title}:${options.body ?? ''}`),
    showOverlayNotification: (payload) =>
      calls.push(
        `overlay:${payload.id}:${payload.historyId}:${payload.title}:${payload.body}:${payload.persistent ? 'pin' : 'auto'}`,
      ),
  });

  assert.deepEqual(calls, [
    'overlay:character-dictionary-auto-sync:character-dictionary-auto-sync-101291-syncing:Character dictionary:syncing:pin',
    'desktop:SubMiner:syncing',
    'overlay:character-dictionary-auto-sync:character-dictionary-auto-sync-101291-ready:Character dictionary:ready:auto',
    'desktop:SubMiner:ready',
  ]);
});

test('auto sync notifications fall back to desktop when overlay routing is unavailable', () => {
  const calls: string[] = [];

  notifyCharacterDictionaryAutoSyncStatus(makeEvent('building', 'building'), {
    getNotificationType: () => undefined,
    showOsd: (message) => {
      calls.push(`osd:${message}`);
    },
    showDesktopNotification: (title, options) =>
      calls.push(`desktop:${title}:${options.body ?? ''}`),
  });

  assert.deepEqual(calls, ['desktop:SubMiner:building']);
});

test('auto sync notifications keep osd-system on legacy surfaces', () => {
  const calls: string[] = [];

  notifyCharacterDictionaryAutoSyncStatus(makeEvent('syncing', 'syncing'), {
    getNotificationType: () => 'osd-system',
    showOsd: (message) => {
      calls.push(`osd:${message}`);
    },
    showDesktopNotification: (title, options) =>
      calls.push(`desktop:${title}:${options.body ?? ''}`),
    showOverlayNotification: (payload) => calls.push(`overlay:${payload.body}`),
  });

  assert.deepEqual(calls, ['osd:syncing', 'desktop:SubMiner:syncing']);
});

test('auto sync notifications keep osd-system desktop delivery even when osd is unavailable', () => {
  const calls: string[] = [];

  notifyCharacterDictionaryAutoSyncStatus(makeEvent('generating', 'generating'), {
    getNotificationType: () => 'osd-system',
    showOsd: (message) => {
      calls.push(`osd:${message}`);
      return false;
    },
    showDesktopNotification: (title, options) =>
      calls.push(`desktop:${title}:${options.body ?? ''}`),
  });
  notifyCharacterDictionaryAutoSyncStatus(makeEvent('ready', 'ready'), {
    getNotificationType: () => 'osd-system',
    showOsd: (message) => {
      calls.push(`osd:${message}`);
      return false;
    },
    showDesktopNotification: (title, options) =>
      calls.push(`desktop:${title}:${options.body ?? ''}`),
  });

  assert.deepEqual(calls, [
    'osd:generating',
    'desktop:SubMiner:generating',
    'osd:ready',
    'desktop:SubMiner:ready',
  ]);
});

test('auto sync notifications send osd-system desktop updates with startup sequencer', () => {
  const calls: string[] = [];

  notifyCharacterDictionaryAutoSyncStatus(makeEvent('importing', 'importing'), {
    getNotificationType: () => 'osd-system',
    showOsd: (message) => {
      calls.push(`osd:${message}`);
    },
    showDesktopNotification: (title, options) =>
      calls.push(`desktop:${title}:${options.body ?? ''}`),
    startupOsdSequencer: {
      notifyCharacterDictionaryStatus: (event) => {
        calls.push(`sequencer:${event.phase}:${event.message}`);
        return false;
      },
    },
  });

  assert.deepEqual(calls, ['sequencer:importing:importing', 'desktop:SubMiner:importing']);
});

test('auto sync notifications let startup sequencer own osd-system desktop delivery', () => {
  const calls: string[] = [];
  const startupOsdSequencer = createStartupOsdSequencer({
    getNotificationType: () => 'osd-system',
    showOsd: (message) => {
      calls.push(`osd:${message}`);
    },
    showDesktopNotification: (title, options) => {
      calls.push(`desktop:${title}:${options.body ?? ''}`);
    },
  });
  startupOsdSequencer.markTokenizationReady();

  notifyCharacterDictionaryAutoSyncStatus(makeEvent('importing', 'importing'), {
    getNotificationType: () => 'osd-system',
    showOsd: (message) => {
      calls.push(`direct-osd:${message}`);
    },
    showDesktopNotification: (title, options) =>
      calls.push(`direct-desktop:${title}:${options.body ?? ''}`),
    startupOsdSequencer,
  });

  assert.deepEqual(calls, ['osd:importing', 'desktop:SubMiner:importing']);
});

test('auto sync desktop notifications reuse one replace id across every phase', () => {
  const replaceIds: Array<string | undefined> = [];
  const deps = {
    getNotificationType: () => 'system' as const,
    showOsd: () => undefined,
    showDesktopNotification: (_title: string, options: { body?: string; replaceId?: string }) => {
      replaceIds.push(options.replaceId);
    },
  };

  for (const phase of ['checking', 'generating', 'importing', 'ready'] as const) {
    notifyCharacterDictionaryAutoSyncStatus(makeEvent(phase, phase), deps);
  }

  assert.deepEqual(replaceIds, [
    'character-dictionary-auto-sync',
    'character-dictionary-auto-sync',
    'character-dictionary-auto-sync',
    'character-dictionary-auto-sync',
  ]);
});

test('overlay-unavailable desktop fallback shares the same replace id', () => {
  const replaceIds: Array<string | undefined> = [];

  notifyCharacterDictionaryAutoSyncStatus(makeEvent('generating', 'generating'), {
    getNotificationType: () => 'overlay',
    showOsd: () => undefined,
    showDesktopNotification: (_title, options) => {
      replaceIds.push(options.replaceId);
    },
  });

  assert.deepEqual(replaceIds, ['character-dictionary-auto-sync']);
});

test('startup lanes keep one desktop notification per lane', () => {
  const calls: Array<{ body?: string; replaceId?: string }> = [];
  const sequencer = createStartupOsdSequencer({
    getNotificationType: () => 'system',
    showOsd: () => undefined,
    showDesktopNotification: (_title, options) => {
      calls.push(options);
    },
  });

  sequencer.markTokenizationReady();
  notifyCharacterDictionaryAutoSyncStatus(makeEvent('generating', 'generating one'), {
    getNotificationType: () => 'osd',
    showOsd: () => undefined,
    showDesktopNotification: () => undefined,
    startupOsdSequencer: sequencer,
  });
  notifyCharacterDictionaryAutoSyncStatus(makeEvent('generating', 'generating two'), {
    getNotificationType: () => 'osd',
    showOsd: () => undefined,
    showDesktopNotification: () => undefined,
    startupOsdSequencer: sequencer,
  });

  assert.deepEqual(
    calls.map((call) => call.replaceId),
    ['startup-status', 'startup-status'],
  );
});
