import { describe, it, expect, stub, assertSpyCalls, assertSpyCallArgs } from '#test';
import { Notification } from './notification.ts';


let fetchSpy: ReturnType<typeof createSpy>;
let response: () => Promise<Response>;
const createSpy = () => stub(globalThis, 'fetch', () => response());

const suite = describe({
  name: 'notification',
  beforeEach() {
    fetchSpy = createSpy();
    response = () => Promise.resolve(new Response('', { status: 200 }));
  },
  afterEach() {
    fetchSpy.restore();
  }
});

it(suite, 'should send ticket with click', async () => {
  const notify = new Notification({
    clusterUrl: 'http://localhost:1',
    market: 'se',
    session: () => ({ customerKey: 'c1', sessionKey: 's1' })
  });

  const result = await notify.click('ticket:1');

  (result satisfies void);
  assertSpyCalls(fetchSpy, 1);
  assertSpyCallArgs(fetchSpy, 0, [
    new URL('http://localhost:1/api/storefront/v3/notifications/click?market=se&customerKey=c1&sessionKey=s1'),
    { method: 'POST', body: '{"ticket":"ticket:1"}', keepalive: true }
  ]);
});

it(suite, 'should send page and list on impression', async () => {
  const notify = new Notification({
    clusterUrl: 'http://localhost:1',
    market: 'se',
    session: () => ({ customerKey: 'c1', sessionKey: 's1' })
  });

  const result = await notify.adImpression('ticket:1');

  (result satisfies void);
  assertSpyCalls(fetchSpy, 1);
  assertSpyCallArgs(fetchSpy, 0, [
    new URL('http://localhost:1/api/storefront/v3/notifications/ad-impression?market=se&customerKey=c1&sessionKey=s1'),
    { method: 'POST', body: '{"ticket":"ticket:1"}', keepalive: true }
  ]);
});

it(suite, 'should send ticket with add-to-cart', async () => {
  const notify = new Notification({
    clusterUrl: 'http://localhost:2',
    market: 'us',
    session: () => ({ customerKey: 'c2', sessionKey: 's2' })
  });

  const result = await notify.addToCart('ticket:2');

  (result satisfies void);
  assertSpyCalls(fetchSpy, 1);
  assertSpyCallArgs(fetchSpy, 0, [
    new URL('http://localhost:2/api/storefront/v3/notifications/add-to-cart?market=us&customerKey=c2&sessionKey=s2'),
    { method: 'POST', body: '{"ticket":"ticket:2"}', keepalive: true }
  ]);
});

it(suite, 'should send add favorite notification with product-key', async () => {
  const notify = new Notification({
    clusterUrl: 'http://localhost:3',
    market: 'no',
    session: () => ({ customerKey: 'c3', sessionKey: 's3' })
  });

  const result = await notify.addFavorite('pk:3');

  (result satisfies void);
  assertSpyCalls(fetchSpy, 1);
  assertSpyCallArgs(fetchSpy, 0, [
    new URL('http://localhost:3/api/storefront/v3/notifications/add-favorite?market=no&customerKey=c3&sessionKey=s3'),
    { method: 'POST', body: '{"productKey":"pk:3"}', keepalive: true }
  ]);

  await notify.addFavorite({ productKey: 'pk:3-2' });

  assertSpyCalls(fetchSpy, 2);
  assertSpyCallArgs(fetchSpy, 1, [
    new URL('http://localhost:3/api/storefront/v3/notifications/add-favorite?market=no&customerKey=c3&sessionKey=s3'),
    { method: 'POST', body: '{"productKey":"pk:3-2"}', keepalive: true }
  ]);
});

it(suite, 'should send remove favorite notification with product-key', async () => {
  const notify = new Notification({
    clusterUrl: 'http://localhost:4',
    market: 'dk',
    session: () => ({ customerKey: 'c4', sessionKey: 's4' })
  });

  const result = await notify.removeFavorite('pk:4');

  (result satisfies void);
  assertSpyCalls(fetchSpy, 1);
  assertSpyCallArgs(fetchSpy, 0, [
    new URL('http://localhost:4/api/storefront/v3/notifications/remove-favorite?market=dk&customerKey=c4&sessionKey=s4'),
    { method: 'POST', body: '{"productKey":"pk:4"}', keepalive: true }
  ]);

  await notify.removeFavorite({ productKey: 'pk:4-2' });

  assertSpyCalls(fetchSpy, 2);
  assertSpyCallArgs(fetchSpy, 1, [
    new URL('http://localhost:4/api/storefront/v3/notifications/remove-favorite?market=dk&customerKey=c4&sessionKey=s4'),
    { method: 'POST', body: '{"productKey":"pk:4-2"}', keepalive: true }
  ]);
});

it(suite, 'should send add favorite notification with variant-key', async () => {
  const notify = new Notification({
    clusterUrl: 'http://localhost:5',
    market: 'fi',
    session: () => ({ customerKey: 'c5', sessionKey: 's5' })
  });

  const result = await notify.addFavorite({ variantKey: 'vk:5' });

  (result satisfies void);
  assertSpyCalls(fetchSpy, 1);
  assertSpyCallArgs(fetchSpy, 0, [
    new URL('http://localhost:5/api/storefront/v3/notifications/add-favorite?market=fi&customerKey=c5&sessionKey=s5'),
    { method: 'POST', body: '{"variantKey":"vk:5"}', keepalive: true }
  ]);
});

it(suite, 'should send remove favorite notification with variant-key', async () => {
  const notify = new Notification({
    clusterUrl: 'http://localhost:6',
    market: 'de',
    session: () => ({ customerKey: 'c6', sessionKey: 's6' })
  });

  const result = await notify.removeFavorite({ variantKey: 'vk:6' });

  (result satisfies void);
  assertSpyCalls(fetchSpy, 1);
  assertSpyCallArgs(fetchSpy, 0, [
    new URL('http://localhost:6/api/storefront/v3/notifications/remove-favorite?market=de&customerKey=c6&sessionKey=s6'),
    { method: 'POST', body: '{"variantKey":"vk:6"}', keepalive: true }
  ]);
});

it(suite, 'should send remove recent search notification with correct payload', async () => {
  const notify = new Notification({
    clusterUrl: 'http://localhost:7',
    market: 'it',
    session: () => ({ customerKey: 'c7', sessionKey: 's7' })
  });

  const result = await notify.removeRecentSearches(['foo', 'bar', 'baz']);

  (result satisfies void);
  assertSpyCalls(fetchSpy, 1);
  assertSpyCallArgs(fetchSpy, 0, [
    new URL('http://localhost:7/api/storefront/v3/notifications/remove-recent-searches?market=it&customerKey=c7&sessionKey=s7'),
    { method: 'POST', body: '{"phrases":["foo","bar","baz"]}', keepalive: true }
  ]);

  await notify.removeRecentSearches('removeAll');

  assertSpyCalls(fetchSpy, 2);
  assertSpyCallArgs(fetchSpy, 1, [
    new URL('http://localhost:7/api/storefront/v3/notifications/remove-recent-searches?market=it&customerKey=c7&sessionKey=s7'),
    { method: 'POST', body: '{"removeAll":true}', keepalive: true }
  ]);
});

it(suite, 'should send remove recently viewed notification with correct payload', async () => {
  const notify = new Notification({
    clusterUrl: 'http://localhost:9',
    market: 'cr',
    session: () => ({ customerKey: 'c9', sessionKey: 's9' })
  });

  const result = await notify.removeRecentlyViewed(['🙌', '🤨']);

  (result satisfies void);
  assertSpyCalls(fetchSpy, 1);
  assertSpyCallArgs(fetchSpy, 0, [
    new URL('http://localhost:9/api/storefront/v3/notifications/remove-recently-viewed?market=cr&customerKey=c9&sessionKey=s9'),
    { method: 'POST', body: '{"productKeys":["🙌","🤨"]}', keepalive: true }
  ]);

  await notify.removeRecentlyViewed('removeAll');

  assertSpyCalls(fetchSpy, 2);
  assertSpyCallArgs(fetchSpy, 1, [
    new URL('http://localhost:9/api/storefront/v3/notifications/remove-recently-viewed?market=cr&customerKey=c9&sessionKey=s9'),
    { method: 'POST', body: '{"removeAll":true}', keepalive: true }
  ]);
});

it(suite, 'should send end notification', async () => {
  const notify = new Notification({
    clusterUrl: 'http://localhost:8',
    market: 'fr',
    session: () => ({ customerKey: 'c8', sessionKey: 's8' })
  });

  const result = await notify.end();

  (result satisfies void);
  assertSpyCalls(fetchSpy, 1);
  assertSpyCallArgs(fetchSpy, 0, [
    new URL('http://localhost:8/api/storefront/v3/notifications/end?market=fr&customerKey=c8&sessionKey=s8'),
    { method: 'POST', body: undefined, keepalive: true }
  ]);
});

it.ignore(suite, 'should reject promise if response is not "ok"', async () => { });

it(suite, 'should retry once with keepalive:false if fetch fails', async () => {
  const notify = new Notification({
    clusterUrl: 'http://localhost:9',
    market: 'cr',
    session: () => ({ customerKey: 'c9', sessionKey: 's9' })
  });

  const responses = [
    Promise.resolve(new Response('', { status: 200 })),
    Promise.reject(new TypeError('Failed to fetch'))
  ];
  response = () => responses.pop()!;

  await notify.click('--ticket--');

  assertSpyCalls(fetchSpy, 2);
  assertSpyCallArgs(fetchSpy, 0, [
    new URL('http://localhost:9/api/storefront/v3/notifications/click?market=cr&customerKey=c9&sessionKey=s9'),
    { method: 'POST', body: '{"ticket":"--ticket--"}', keepalive: true }
  ]);
  assertSpyCallArgs(fetchSpy, 1, [
    new URL('http://localhost:9/api/storefront/v3/notifications/click?market=cr&customerKey=c9&sessionKey=s9'),
    { method: 'POST', body: '{"ticket":"--ticket--"}', keepalive: false }
  ]);
});

it(suite, 'should throw on notify when both fetch and retry (with keepalive:false) fails', async () => {
  const notify = new Notification({
    clusterUrl: 'http://localhost:10',
    market: 'ch',
    session: () => ({ customerKey: 'c10', sessionKey: 's10' })
  });

  const fetchError = new TypeError('Failed to fetch');
  response = () => Promise.reject(fetchError);

  await expect(notify.click('--ticket--')).rejects.toStrictEqual(fetchError);

  assertSpyCalls(fetchSpy, 2);
  assertSpyCallArgs(fetchSpy, 0, [
    new URL('http://localhost:10/api/storefront/v3/notifications/click?market=ch&customerKey=c10&sessionKey=s10'),
    { method: 'POST', body: '{"ticket":"--ticket--"}', keepalive: true }
  ]);
  assertSpyCallArgs(fetchSpy, 1, [
    new URL('http://localhost:10/api/storefront/v3/notifications/click?market=ch&customerKey=c10&sessionKey=s10'),
    { method: 'POST', body: '{"ticket":"--ticket--"}', keepalive: false }
  ]);
});
