import { get } from 'svelte/store';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

import {
	clearInstallPrompt,
	dismissIosInstallHint,
	dismissOfflineReady,
	promptInstall,
	pwaState,
	setInstallPrompt,
	setOfflineReady,
	setOnlineStatus,
	setStandaloneMode,
	setUpdateAvailable,
	syncInstallHelpState
} from './pwa';

const originalNavigator = globalThis.navigator;
const originalWindow = globalThis.window;
const originalLocalStorage = globalThis.localStorage;

const createStorage = () => {
	const values = new Map<string, string>();
	return {
		getItem: (key: string) => values.get(key) ?? null,
		setItem: (key: string, value: string) => values.set(key, value),
		removeItem: (key: string) => values.delete(key),
		clear: () => values.clear()
	};
};

const installPromptFactory = (outcome: 'accepted' | 'dismissed' = 'accepted') => ({
	prompt: vi.fn().mockResolvedValue(undefined),
	userChoice: Promise.resolve({ outcome })
});

beforeEach(() => {
	vi.restoreAllMocks();
	const localStorage = createStorage();
	vi.stubGlobal('localStorage', localStorage);
	vi.stubGlobal('navigator', { userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 Safari/604.1' });
	vi.stubGlobal('window', {
		matchMedia: vi.fn().mockReturnValue({ matches: false }),
		navigator: { standalone: false }
	});

	pwaState.set({
		installAvailable: false,
		updateAvailable: false,
		offlineReady: false,
		installing: false,
		isOffline: false,
		isStandalone: false,
		showIosInstallHint: false
	});
	clearInstallPrompt();
});

afterEach(() => {
	clearInstallPrompt();
	if (originalNavigator) {
		Object.defineProperty(globalThis, 'navigator', { value: originalNavigator, configurable: true });
	} else {
		// @ts-ignore
		delete globalThis.navigator;
	}
	if (originalWindow) {
		Object.defineProperty(globalThis, 'window', { value: originalWindow, configurable: true });
	} else {
		// @ts-ignore
		delete globalThis.window;
	}
	if (originalLocalStorage) {
		Object.defineProperty(globalThis, 'localStorage', { value: originalLocalStorage, configurable: true });
	} else {
		// @ts-ignore
		delete globalThis.localStorage;
	}
});

describe('pwa state', () => {
	it('stores install prompt availability', () => {
		setInstallPrompt(installPromptFactory());

		expect(get(pwaState).installAvailable).toBe(true);
	});

	it('clears install prompt state', () => {
		setInstallPrompt(installPromptFactory());
		clearInstallPrompt();

		const state = get(pwaState);
		expect(state.installAvailable).toBe(false);
		expect(state.installing).toBe(false);
	});

	it('tracks update, offline ready, online, and standalone state flags', () => {
		setUpdateAvailable(true);
		setOfflineReady(true);
		setOnlineStatus(false);
		setStandaloneMode(true);

		let state = get(pwaState);
		expect(state.updateAvailable).toBe(true);
		expect(state.offlineReady).toBe(true);
		expect(state.isOffline).toBe(true);
		expect(state.isStandalone).toBe(true);

		dismissOfflineReady();
		state = get(pwaState);
		expect(state.offlineReady).toBe(false);
	});

	it('shows iOS install help when running in Safari outside standalone mode', () => {
		syncInstallHelpState();

		expect(get(pwaState).showIosInstallHint).toBe(true);
	});

	it('dismisses iOS install help and persists the dismissal', () => {
		syncInstallHelpState();
		dismissIosInstallHint();

		const state = get(pwaState);
		expect(state.showIosInstallHint).toBe(false);
		expect(globalThis.localStorage.getItem('phonecam-ios-install-hint-dismissed')).toBe('true');
	});

	it('returns false when install prompt is unavailable', async () => {
		await expect(promptInstall()).resolves.toBe(false);
	});

	it('prompts installation and clears the deferred prompt after acceptance', async () => {
		const prompt = installPromptFactory('accepted');
		setInstallPrompt(prompt);

		await expect(promptInstall()).resolves.toBe(true);

		const state = get(pwaState);
		expect(prompt.prompt).toHaveBeenCalledTimes(1);
		expect(state.installAvailable).toBe(false);
		expect(state.installing).toBe(false);
	});
});
