import { Capacitor } from '@capacitor/core';
import { writable, type Writable } from 'svelte/store';
import { Preferences } from '@capacitor/preferences';
import {
	persist,
	createLocalStorage,
	type StorageInterface,
	type SelfUpdateStorageInterface
} from '@macfja/svelte-persistent-store';
import type { TitleCase } from './letterCasing';
import { serialize, deserialize } from '@macfja/serializer';
import type {
	ChannelContent,
	ChannelPage,
	PlaylistPage,
	PlaylistPageVideo,
	SearchResults,
	Video,
	VideoBase,
	VideoPlay
} from './api/model';
import type { ParsedDescription } from './description';
import { ensureNoTrailingSlash, getPublicEnv } from './misc';
import type { EngineFallback } from './api/misc';
import type z from 'zod';
import type { zFilterSchema } from './filtering/index';
import type { ThemeColors } from './theme';

function createListenerFunctions(): {
	callListeners: (eventKey: string, newValue: any) => void;
	addListener: (key: string, listener: (newValue: any) => void) => void;
	removeListener: (key: string, listener: (newValue: any) => void) => void;
} {
	const listeners: Array<{ key: string; listener: (newValue: any) => void }> = [];
	return {
		callListeners(eventKey: string, newValue: any) {
			if (newValue === undefined) {
				return;
			}
			listeners.filter(({ key }) => key === eventKey).forEach(({ listener }) => listener(newValue));
		},
		addListener(key: string, listener: (newValue: any) => void) {
			listeners.push({ key, listener });
		},
		removeListener(key: string, listener: (newValue: any) => void) {
			const index = listeners.indexOf({ key, listener });
			if (index !== -1) {
				listeners.splice(index, 1);
			}
		}
	};
}

function createStorage(): StorageInterface<any> | SelfUpdateStorageInterface<any> {
	if (Capacitor.getPlatform() === 'android') {
		// https://github.com/MacFJA/svelte-persistent-store/blob/main/.docs/How-To/02-New-Async-Storage.md
		const { removeListener, callListeners, addListener } = createListenerFunctions();
		return {
			getValue(key: string): any | null {
				Preferences.get({ key: key }).then((value) => {
					if (value.value !== null) {
						callListeners(key, deserialize(value.value));
					}
				});

				return null;
			},
			deleteValue(key: string) {
				Preferences.remove({ key: key });
			},
			setValue(key: string, value: any) {
				Preferences.set({ key: key, value: serialize(value) });
			},
			addListener,
			removeListener
		};
	} else {
		return createLocalStorage(true);
	}
}

function ifNotWebDefault(givenValue: any, defaultValue: any): any {
	if (typeof givenValue !== 'undefined' && givenValue !== null) {
		return givenValue;
	} else if (defaultValue && Capacitor.getPlatform() !== 'web') {
		return defaultValue;
	}
}

export const invidiousInstanceStore: Writable<string | undefined> = persist(
	writable(
		!getPublicEnv('DEFAULT_INVIDIOUS_INSTANCE')
			? undefined
			: ensureNoTrailingSlash(getPublicEnv('DEFAULT_INVIDIOUS_INSTANCE'))
	),
	createStorage(),
	'invidiousInstance'
);

export const backendInUseStore: Writable<'ivg' | 'yt'> = persist(
	writable('ivg'),
	createStorage(),
	'backendInUse'
);

export const invidiousAuthStore: Writable<null | { username: string; token: string }> = persist(
	writable(null),
	createStorage(),
	'authToken'
);

export const darkModeStore: Writable<null | boolean> = persist(
	writable(null),
	createStorage(),
	'darkMode'
);
export const themeColorStore: Writable<null | string> = persist(
	writable(null),
	createStorage(),
	'themeColor'
);

export const showWarningStore = persist(writable(true), createStorage(), 'showWarning');

export const playerAutoPlayStore = persist(writable(true), createStorage(), 'autoPlay');
export const playerAlwaysLoopStore = persist(writable(false), createStorage(), 'alwaysLoop');
export const playerProxyVideosStore = persist(writable(true), createStorage(), 'proxyVideos');
export const playerSavePlaybackPositionStore = persist(
	writable(true),
	createStorage(),
	'savePlaybackPosition'
);
export const playerTheatreModeByDefaultStore = persist(
	writable(false),
	createStorage(),
	'theatreModeByDefault'
);
export const playerDefaultQualityStore = persist(
	writable('auto'),
	createStorage(),
	'defaultQuality'
);
export const playerAutoplayNextByDefaultStore = persist(
	writable(false),
	createStorage(),
	'autoplayNextByDefault'
);
export const playerYouTubeJsFallback = persist(
	writable(true),
	createStorage(),
	'youTubeJsFallback'
);
export const playerYouTubeJsAlways = persist(writable(false), createStorage(), 'youTubeJsAlways');
export const playerAndroidLockOrientation = persist(
	writable(true),
	createStorage(),
	'androidLockOrientation'
);
export const playerDefaultLanguage = persist(
	writable('original'),
	createStorage(),
	'defaultLanguage'
);
export const playerCCByDefault = persist(writable(false), createStorage(), 'CCByDefault');
export const playerDefaultPlaybackSpeed: Writable<number> = persist(
	writable(1),
	createStorage(),
	'defaultPlaybackSpeed'
);
export const playerMiniplayerEnabled = persist(
	writable(true),
	createStorage(),
	'miniplayerEnabled'
);
export const playerAndroidPauseOnNetworkChange = persist(
	writable(true),
	createStorage(),
	'pauseOnNetworkChange'
);
export const playerPreferredVolumeStore: Writable<number> = persist(
	writable(0.5),
	createStorage(),
	'preferredVolume'
);
export const playerPlaylistHistory: Writable<string[]> = writable([]);

export interface PlayerState {
	data: { video: VideoPlay; content: ParsedDescription; playlistId: string | null };
	playerElement?: HTMLMediaElement | undefined;
}

export const playerState: Writable<PlayerState | undefined> = writable(undefined);
export const playerTheatreModeIsActive = writable(false);

export const returnYtDislikesStore = persist(writable(false), createStorage(), 'returnYtDislikes');
export const returnYTDislikesInstanceStore: Writable<string | null | undefined> = persist(
	writable(
		ifNotWebDefault(
			getPublicEnv('DEFAULT_RETURNYTDISLIKES_INSTANCE'),
			'https://ryd-proxy.materialio.us'
		)
	),
	createStorage(),
	'returnYTDislikesInstance'
);

export const interfaceRegionStore: Writable<string> = persist(
	writable('US'),
	createStorage(),
	'interfaceRegion'
);
export const interfaceSearchSuggestionsStore = persist(
	writable(true),
	createStorage(),
	'searchSuggestions'
);
export const interfaceForceCase: Writable<TitleCase> = persist(
	writable('original'),
	createStorage(),
	'forceCase'
);
export const interfaceAutoExpandComments: Writable<boolean> = persist(
	writable(true),
	createStorage(),
	'autoExpandComments'
);
export const interfaceAutoExpandDesc: Writable<boolean> = persist(
	writable(false),
	createStorage(),
	'autoExpandDesc'
);
export const interfaceAutoExpandChapters: Writable<boolean> = persist(
	writable(false),
	createStorage(),
	'autoExpandChapters'
);
export const interfaceAmoledTheme = persist(writable(false), createStorage(), 'amoledTheme');
export const interfaceDisplayThumbnailAvatars = persist(
	writable(false),
	createStorage(),
	'disableThumbnailAvatars'
);
export const interfaceDefaultPage = persist(writable('/'), createStorage(), 'defaultPage');
export const interfaceSearchHistoryEnabled = persist(
	writable(false),
	createStorage(),
	'searchHistoryEnabled'
);
export const interfaceAllowInsecureRequests = persist(
	writable(false),
	createStorage(),
	'allowInsecureRequests'
);
export const interfaceDisableAutoUpdate = persist(
	writable(false),
	createStorage(),
	'disableAutoUpdate'
);
export const interfaceAndroidUseNativeShare = persist(
	writable(true),
	createStorage(),
	'androidUseNativeShare'
);
export const interfaceAdvancedThemingStore: Writable<ThemeColors> = persist(
	writable({}),
	createStorage(),
	'advancedTheming'
);
export const interfaceBorderRadiusStore: Writable<number> = persist(
	writable(0.5),
	createStorage(),
	'borderRadius'
);
export const interfacePreserveTranslation: Writable<boolean> = persist(
	writable(false),
	createStorage(),
	'preserveTranslation'
);

export const sponsorBlockStore = persist(writable(true), createStorage(), 'sponsorBlock');
export const sponsorBlockUrlStore: Writable<string | null | undefined> = persist(
	writable(getPublicEnv('DEFAULT_SPONSERBLOCK_INSTANCE') || 'https://sponsor.ajay.app'),
	createStorage(),
	'sponsorBlockUrl'
);

export const sponsorBlockCategoriesStore: Writable<
	Record<string, 'automatic' | 'manual' | 'timeline' | undefined>
> = persist(writable({}), createStorage(), 'sponsorBlockCategoriesv2');

export const sponsorBlockDisplayToastStore: Writable<boolean> = persist(
	writable(false),
	createStorage(),
	'sponsorBlockDisplayToast'
);
export const sponsorBlockTimelineStore: Writable<boolean> = persist(
	writable(false),
	createStorage(),
	'sponsorBlockTimeline'
);

export const deArrowInstanceStore = persist(
	writable(getPublicEnv('DEFAULT_DEARROW_INSTANCE') || 'https://sponsor.ajay.app'),
	createStorage(),
	'deArrowInstance'
);
export const deArrowEnabledStore = persist(writable(false), createStorage(), 'deArrowEnabled');
export const deArrowTitlesOnly = persist(writable(true), createStorage(), 'deArrowTitlesOnly');
export const deArrowThumbnailInstanceStore = persist(
	writable(getPublicEnv('DEFAULT_DEARROW_THUMBNAIL_INSTANCE') || 'https://dearrow-thumb.ajay.app'),
	createStorage(),
	'deArrowThumbnailInstance'
);

export const engineCullYTStore: Writable<number> = persist(
	writable(500),
	createStorage(),
	'engineCullYT'
);
export const engineCooldownYTStore: Writable<number> = persist(
	writable(6),
	createStorage(),
	'engineCooldownYT'
);
export const engineMaxConcurrentChannelsStore: Writable<number> = persist(
	writable(50),
	createStorage(),
	'engineMaxConcurrentChannels'
);

export const engineFallbacksStore: Writable<EngineFallback[]> = persist(
	writable([]),
	createStorage(),
	'engineFallbacks'
);

export const rawMasterKeyStore: Writable<string | undefined> = persist(
	writable(),
	createStorage(),
	'rawMasterKey'
);

export const watchHistoryEnabledStore: Writable<boolean> = persist(
	writable(true),
	createStorage(),
	'watchHistoryEnabled'
);

export const playlistSettingsStore: Writable<Record<string, { shuffle: boolean; loop: boolean }>> =
	writable({});

export const poTokenCacheStore: Writable<string | undefined> = writable();

export const searchHistoryStore: Writable<string[]> = persist(
	writable([]),
	createStorage(),
	'searchHistory'
);

export const filterContentListStore: Writable<z.infer<typeof zFilterSchema> | undefined> = persist(
	writable(),
	createStorage(),
	'filterContentList'
);

export const filterContentUrlStore: Writable<string | undefined> = persist(
	writable(),
	createStorage(),
	'filterContentUrl'
);

export const filterContentUrlAutoUpdateStore: Writable<boolean> = persist(
	writable(false),
	createStorage(),
	'filterContentUrlAutoUpdate'
);

export const feedLoadingStore: Writable<boolean> = writable(false);
export const feedCacheStore: Writable<{
	[key: string]: (VideoBase | Video | PlaylistPageVideo)[];
}> = writable({});
export const searchCacheStore: Writable<{
	[searchTypeAndQuery: string]: SearchResults;
}> = writable({});
export const feedLastItemId: Writable<string | undefined> = writable(undefined);
export const playlistCacheStore: Writable<{
	[playlistId: string]: { videos: PlaylistPageVideo[]; info: PlaylistPage };
}> = writable({});

export const isAndroidTvStore: Writable<boolean> = writable(false);

export const channelCacheStore: Writable<{
	[key: string]: {
		channel: ChannelPage;
		displayContent: { [key: string]: ChannelContent };
	};
}> = writable({});

export const hideSearchStore: Writable<boolean> = writable(false);
