// #region Test.Health [C:3] [TYPE Module] [SEMANTICS test, health, store, summary, badge]
// @BRIEF Unit tests for health store — initial state, refresh/dedup, error handling, subscriber contracts.
// @RELATION DEPENDS_ON -> [HealthStore]
// @TEST_EDGE: missing_field -> refresh returns null when api rejects with 404
// @TEST_EDGE: invalid_type -> getHealthSummary returns unexpected shape, assertions validate key fields
// @TEST_EDGE: external_fail -> api.getHealthSummary rejects with generic error, store recovers gracefully

import { describe, it, expect, beforeEach, vi } from 'vitest';

vi.mock('$lib/api', () => ({
	api: {
		getHealthSummary: vi.fn(),
	},
}));

describe('health store', () => {
	beforeEach(() => {
		vi.resetModules();
	});

	// ── Initial state ────────────────────────────────────────────────

	it('should have correct initial state', async () => {
		const { healthStore } = await import('../health.svelte.js');

		expect(healthStore.items).toEqual([]);
		expect(healthStore.pass_count).toBe(0);
		expect(healthStore.warn_count).toBe(0);
		expect(healthStore.fail_count).toBe(0);
		expect(healthStore.unknown_count).toBe(0);
		expect(healthStore.loading).toBe(false);
		expect(healthStore.lastUpdated).toBeNull();
	});

	// ── Subscribe contracts ──────────────────────────────────────────

	it('healthStore.subscribe should call fn immediately with current state', async () => {
		const { healthStore } = await import('../health.svelte.js');

		const spy = vi.fn();
		const unsubscribe = healthStore.subscribe(spy);
		expect(spy).toHaveBeenCalledTimes(1);
		unsubscribe();
	});

	it('failingCount.subscribe should call fn immediately with current fail count', async () => {
		const { failingCount } = await import('../health.svelte.js');

		const spy = vi.fn();
		const unsubscribe = failingCount.subscribe(spy);
		expect(spy).toHaveBeenCalledTimes(1);
		expect(spy).toHaveBeenCalledWith(0);
		unsubscribe();
	});

	// ── Refresh ──────────────────────────────────────────────────────

	it('refresh() should call api.getHealthSummary and update state', async () => {
		const { api } = await import('$lib/api');
		vi.mocked(api.getHealthSummary).mockClear();
		const mockSummary = {
			items: [{ id: '1', status: 'ok' }],
			pass_count: 5,
			warn_count: 2,
			fail_count: 1,
			unknown_count: 0,
			loading: false,
		};
		vi.mocked(api.getHealthSummary).mockResolvedValue(mockSummary);

		const { healthStore } = await import('../health.svelte.js');

		const result = await healthStore.refresh('env-1');

		expect(api.getHealthSummary).toHaveBeenCalledWith('env-1');
		expect(result).toEqual(mockSummary);
		expect(healthStore.items).toEqual(mockSummary.items);
		expect(healthStore.pass_count).toBe(5);
		expect(healthStore.warn_count).toBe(2);
		expect(healthStore.fail_count).toBe(1);
		expect(healthStore.unknown_count).toBe(0);
		expect(healthStore.loading).toBe(false);
		expect(healthStore.lastUpdated).toBeInstanceOf(Date);
	});

	// ── Dedup ────────────────────────────────────────────────────────

	it('refresh dedup: same environmentId should not trigger second API call', async () => {
		const { api } = await import('$lib/api');
		vi.mocked(api.getHealthSummary).mockClear();
		let resolvePromise!: (v: unknown) => void;
		const pendingPromise = new Promise((resolve) => {
			resolvePromise = resolve;
		});
		vi.mocked(api.getHealthSummary).mockReturnValue(pendingPromise);

		const { healthStore } = await import('../health.svelte.js');

		// Start two concurrent refreshes — second should hit dedup
		const promise1 = healthStore.refresh('env-1');
		const promise2 = healthStore.refresh('env-1');

		// API should only be called ONCE (dedup prevents second call)
		expect(api.getHealthSummary).toHaveBeenCalledTimes(1);

		// Both promises should resolve to the same result
		const expected = {
			items: [],
			pass_count: 0,
			warn_count: 0,
			fail_count: 0,
			unknown_count: 0,
		};
		resolvePromise(expected);

		const result1 = await promise1;
		const result2 = await promise2;
		expect(result1).toEqual(expected);
		expect(result2).toEqual(expected);
	});

	it('refresh dedup: different environmentId should trigger separate calls', async () => {
		const { api } = await import('$lib/api');
		vi.mocked(api.getHealthSummary).mockClear();
		let resolve1!: (v: unknown) => void;
		let resolve2!: (v: unknown) => void;
		vi.mocked(api.getHealthSummary)
			.mockReturnValueOnce(new Promise((r) => { resolve1 = r; }))
			.mockReturnValueOnce(new Promise((r) => { resolve2 = r; }));

		const { healthStore } = await import('../health.svelte.js');

		healthStore.refresh('env-1');
		healthStore.refresh('env-2');

		// Two different env IDs → two API calls
		expect(api.getHealthSummary).toHaveBeenCalledTimes(2);

		resolve1!(null);
		resolve2!(null);
	});

	// ── 404 / disabled ───────────────────────────────────────────────

	it('refresh error with 404 message should disable further refreshes', async () => {
		const { api } = await import('$lib/api');
		vi.mocked(api.getHealthSummary).mockClear();
		vi.mocked(api.getHealthSummary).mockRejectedValue(
			new Error('404: Health monitor feature is disabled'),
		);

		const { healthStore } = await import('../health.svelte.js');

		const result = await healthStore.refresh();
		expect(result).toBeNull();
		expect(healthStore.loading).toBe(false);

		// Subsequent refresh should return null without calling api
		const result2 = await healthStore.refresh();
		expect(result2).toBeNull();
		expect(api.getHealthSummary).toHaveBeenCalledTimes(1);
	});

	// ── Generic error ────────────────────────────────────────────────

	it('refresh general error should set loading false and return null', async () => {
		const { api } = await import('$lib/api');
		vi.mocked(api.getHealthSummary).mockClear();
		vi.mocked(api.getHealthSummary).mockRejectedValue(new Error('Network error'));

		const { healthStore } = await import('../health.svelte.js');

		const result = await healthStore.refresh();
		expect(result).toBeNull();
		expect(healthStore.loading).toBe(false);

		// Non-404 errors should NOT disable refreshes (can retry)
		const result2 = await healthStore.refresh();
		expect(result2).toBeNull();
		expect(api.getHealthSummary).toHaveBeenCalledTimes(2);
	});

	// ── Failing count after refresh ──────────────────────────────────

	it('failingCount.subscribe should pass current fail_count after refresh', async () => {
		const { api } = await import('$lib/api');
		vi.mocked(api.getHealthSummary).mockClear();
		vi.mocked(api.getHealthSummary).mockResolvedValue({
			items: [],
			pass_count: 10,
			warn_count: 1,
			fail_count: 3,
			unknown_count: 0,
		});

		const { healthStore, failingCount } = await import('../health.svelte.js');
		await healthStore.refresh();

		const spy = vi.fn();
		const unsubscribe = failingCount.subscribe(spy);
		expect(spy).toHaveBeenCalledWith(3);
		unsubscribe();
	});

	// ── Refresh with null environmentId ───────────────────────────────

	it('refresh with null environmentId should call api without argument', async () => {
		const { api } = await import('$lib/api');
		vi.mocked(api.getHealthSummary).mockClear();
		vi.mocked(api.getHealthSummary).mockResolvedValue({
			items: [],
			pass_count: 0,
			warn_count: 0,
			fail_count: 0,
			unknown_count: 0,
		});

		const { healthStore } = await import('../health.svelte.js');

		await healthStore.refresh(null);

		expect(api.getHealthSummary).toHaveBeenCalledWith(undefined);
	});

	// ── _setStateFromSummary with lastUpdated ─────────────────────────

	it('refresh should set lastUpdated after successful fetch', async () => {
		const { api } = await import('$lib/api');
		vi.mocked(api.getHealthSummary).mockClear();
		vi.mocked(api.getHealthSummary).mockResolvedValue({
			items: [{ id: '1' }],
			pass_count: 1,
			warn_count: 0,
			fail_count: 0,
			unknown_count: 0,
		});

		const { healthStore } = await import('../health.svelte.js');
		await healthStore.refresh('env-1');

		expect(healthStore.lastUpdated).toBeInstanceOf(Date);
	});

	// ── refresh 404-specific message ──────────────────────────────────

	it('refresh error with "404" in message should disable further refreshes', async () => {
		const { api } = await import('$lib/api');
		vi.mocked(api.getHealthSummary).mockClear();
		vi.mocked(api.getHealthSummary).mockRejectedValue(new Error('404 Not Found'));

		const { healthStore } = await import('../health.svelte.js');

		await healthStore.refresh();
		expect(healthStore.loading).toBe(false);

		// Subsequent call should be disabled
		const result = await healthStore.refresh();
		expect(result).toBeNull();
		expect(api.getHealthSummary).toHaveBeenCalledTimes(1);
	});

	// ── healthStore.subscribe with initial fn call ────────────────────

	it('healthStore.subscribe should call fn immediately and after refresh', async () => {
		const { api } = await import('$lib/api');
		vi.mocked(api.getHealthSummary).mockClear();
		vi.mocked(api.getHealthSummary).mockResolvedValue({
			items: [],
			pass_count: 3,
			warn_count: 0,
			fail_count: 1,
			unknown_count: 0,
		});

		const { healthStore } = await import('../health.svelte.js');

		const spy = vi.fn();
		const unsubscribe = healthStore.subscribe(spy);
		expect(spy).toHaveBeenCalledTimes(1);

		await healthStore.refresh();
		// subscribe fn is called: once on subscribe (1), once on _loading=true (2), once on result (3)
		expect(spy).toHaveBeenCalledTimes(3);

		unsubscribe();
	});

	// ── _setStateFromSummary with missing items ───────────────────────

	it('refresh with missing items field should default to empty array', async () => {
		const { api } = await import('$lib/api');
		vi.mocked(api.getHealthSummary).mockClear();
		vi.mocked(api.getHealthSummary).mockResolvedValue({
			pass_count: 2,
			warn_count: 1,
			fail_count: 0,
			unknown_count: 0,
		} as any);

		const { healthStore } = await import('../health.svelte.js');
		await healthStore.refresh();

		expect(healthStore.items).toEqual([]);
		expect(healthStore.pass_count).toBe(2);
	});

	// ── _setStateFromSummary with lastUpdated ─────────────────────────

	it('refresh with lastUpdated in summary should set it', async () => {
		const { api } = await import('$lib/api');
		vi.mocked(api.getHealthSummary).mockClear();
		const testDate = new Date('2024-01-15');
		vi.mocked(api.getHealthSummary).mockResolvedValue({
			items: [],
			pass_count: 0,
			warn_count: 0,
			fail_count: 0,
			unknown_count: 0,
			lastUpdated: testDate,
		} as any);

		const { healthStore } = await import('../health.svelte.js');
		await healthStore.refresh('env-1');

		// lastUpdated should be set from summary
		expect(healthStore.lastUpdated).toBeInstanceOf(Date);
	});

	// ── Error without message ─────────────────────────────────────────

	it('refresh error without .message should still be handled', async () => {
		const { api } = await import('$lib/api');
		vi.mocked(api.getHealthSummary).mockClear();
		vi.mocked(api.getHealthSummary).mockRejectedValue('Network error string');

		const { healthStore } = await import('../health.svelte.js');
		const result = await healthStore.refresh();

		expect(result).toBeNull();
		expect(healthStore.loading).toBe(false);
	});

	// ── failingCount.current getter ───────────────────────────────────

	it('failingCount.current should return current fail count', async () => {
		const { api } = await import('$lib/api');
		vi.mocked(api.getHealthSummary).mockClear();
		vi.mocked(api.getHealthSummary).mockResolvedValue({
			items: [],
			pass_count: 0,
			warn_count: 0,
			fail_count: 7,
			unknown_count: 0,
		});

		const { healthStore, failingCount } = await import('../health.svelte.js');
		expect(failingCount.current).toBe(0);

		await healthStore.refresh();
		expect(failingCount.current).toBe(7);
	});

	// ── Subscriber notification coverage ───────────────────────────

	it('subscribe + refresh triggers all subscriber callbacks', async () => {
		const { api } = await import('$lib/api');
		vi.mocked(api.getHealthSummary).mockClear();
		vi.mocked(api.getHealthSummary).mockResolvedValue({
			items: [],
			pass_count: 3,
			warn_count: 1,
			fail_count: 2,
			unknown_count: 0,
		});

		const { healthStore, failingCount } = await import('../health.svelte.js');

		// Subscribe BEFORE refresh — ensures _notifyHealth and _notifyFailingCount
		// receive callbacks to iterate via SvelteSet.forEach
		const healthSpy = vi.fn();
		const failSpy = vi.fn();
		const unsubHealth = healthStore.subscribe(healthSpy);
		const unsubFail = failingCount.subscribe(failSpy);

		expect(healthSpy).toHaveBeenCalledTimes(1); // immediate call
		expect(failSpy).toHaveBeenCalledWith(0); // initial fail_count

		await healthStore.refresh();

		// healthSpy called: immediate(1) + loading(2) + result(3) = 3
		expect(healthSpy).toHaveBeenCalledTimes(3);
		// failSpy called: initial(0) + after refresh(2) = 2
		expect(failSpy).toHaveBeenCalledWith(2);

		unsubHealth();
		unsubFail();
	});

	it('unsubscribing from healthStore stops notifications', async () => {
		const { api } = await import('$lib/api');
		vi.mocked(api.getHealthSummary).mockClear();
		vi.mocked(api.getHealthSummary).mockResolvedValue({
			items: [], pass_count: 0, warn_count: 0, fail_count: 0, unknown_count: 0,
		});

		const { healthStore } = await import('../health.svelte.js');

		const spy = vi.fn();
		const unsubscribe = healthStore.subscribe(spy);
		spy.mockClear();

		unsubscribe();

		await healthStore.refresh();
		expect(spy).not.toHaveBeenCalled();
	});

	it('unsubscribing from failingCount stops notifications', async () => {
		const { api } = await import('$lib/api');
		vi.mocked(api.getHealthSummary).mockClear();
		vi.mocked(api.getHealthSummary).mockResolvedValue({
			items: [], pass_count: 0, warn_count: 0, fail_count: 5, unknown_count: 0,
		});

		const { healthStore, failingCount } = await import('../health.svelte.js');

		const spy = vi.fn();
		const unsubscribe = failingCount.subscribe(spy);
		spy.mockClear();

		unsubscribe();

		await healthStore.refresh();
		expect(spy).not.toHaveBeenCalled();
	});
});
// #endregion Test.Health
