import type { AppBskyActorStatus, AppBskyEmbedExternal } from '@atcute/bluesky';
import { ClientResponseError } from '@atcute/client';
import type { $type, GenericUri } from '@atcute/lexicons';

import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';

import { deleteRecord, getRecord, putRecord, uploadBlob } from '#/lib/api/records';
import { imageToThumb } from '#/lib/api/resolve';
import { getLinkMeta, type LinkMeta } from '#/lib/link-meta';
import { retry } from '#/lib/utils/retry';

import { updateProfileShadow } from '#/state/cache/profile-shadow';
import { getClients, useSession } from '#/state/session';

import { useLiveNowConfig } from '#/features/liveNow/use-actor-status';
import { getLiveServiceNames, isLiveNowUrlAllowed } from '#/features/liveNow/utils';

import type { DialogHandle } from '#/components/Dialog';
import * as Toast from '#/components/Toast';

import { m } from '#/paraglide/messages';

export * from '#/features/liveNow/utils';

export function useLiveLinkMetaQuery(url: string | null) {
	const liveNowConfig = useLiveNowConfig();
	return useQuery({
		queryKey: ['link-meta', url],
		enabled: !!url,
		queryFn: async ({ signal }) => {
			if (!url) {
				return undefined;
			}
			if (!isLiveNowUrlAllowed(url, liveNowConfig.currentAccountAllowedHosts)) {
				const { formatted } = getLiveServiceNames(liveNowConfig.currentAccountAllowedHosts);
				throw new Error(m['features.liveNow.service.unsupported']({ formatted }));
			}

			return await getLinkMeta(url, { signal });
		},
	});
}

export function useUpsertLiveStatusMutation(
	handle: DialogHandle,
	duration: number,
	linkMeta: LinkMeta | null | undefined,
	createdAt?: string,
) {
	const { currentAccount } = useSession();
	const { pds } = getClients();
	const queryClient = useQueryClient();
	return useMutation({
		mutationFn: async () => {
			if (!currentAccount) {
				throw new Error('Not logged in');
			}

			let embed: $type.enforce<AppBskyEmbedExternal.Main> | undefined;

			if (linkMeta) {
				let thumb;

				if (linkMeta.image) {
					try {
						const img = await imageToThumb(linkMeta.image);
						if (img) {
							thumb = await uploadBlob(pds!, img.source.blob);
						}
					} catch (e) {
						console.error('Failed to upload thumbnail for live status', linkMeta.image, e);
					}
				}

				embed = {
					$type: 'app.bsky.embed.external',
					external: {
						$type: 'app.bsky.embed.external#external',
						title: linkMeta.title ?? '',
						description: linkMeta.description ?? '',
						// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- `LinkMeta.url` always carries a scheme
						uri: linkMeta.url as GenericUri,
						thumb,
					},
				};
			}

			const record = {
				$type: 'app.bsky.actor.status',
				status: 'app.bsky.actor.status#live',
				createdAt: createdAt ?? new Date().toISOString(),
				durationMinutes: duration,
				embed,
			} satisfies AppBskyActorStatus.Main;

			const upsert = async () => {
				const repo = currentAccount.did;
				const collection = 'app.bsky.actor.status';

				const existing = await getRecord(pds!, { repo, collection, rkey: 'self' }).catch(() => undefined);

				await putRecord(pds!, {
					repo,
					rkey: 'self',
					record,
					swapRecord: existing?.cid ?? null,
				});
			};

			await retry(5, (e) => e instanceof ClientResponseError && e.error === 'InvalidSwap', upsert);

			return {
				record,
				image: linkMeta?.image,
			};
		},
		onSuccess: ({ record, image }) => {
			Toast.show(m['features.liveNow.goLive.started']());
			handle.close();

			if (!currentAccount) {
				return;
			}

			const expiresAt = new Date(record.createdAt);
			expiresAt.setMinutes(expiresAt.getMinutes() + record.durationMinutes);

			updateProfileShadow(queryClient, currentAccount.did, {
				status: {
					$type: 'app.bsky.actor.defs#statusView',
					status: 'app.bsky.actor.status#live',
					isActive: true,
					expiresAt: expiresAt.toISOString(),
					embed:
						record.embed && image
							? {
									$type: 'app.bsky.embed.external#view',
									external: {
										$type: 'app.bsky.embed.external#viewExternal',
										description: record.embed.external.description,
										// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- the resolver hands back an absolute thumbnail url
										thumb: image as GenericUri,
										title: record.embed.external.title,
										uri: record.embed.external.uri,
									},
								}
							: undefined,
					record,
				},
			});
		},
		onError: (e) => {
			console.error('Failed to upsert live status', linkMeta?.url, e);
		},
	});
}

export function useRemoveLiveStatusMutation(handle: DialogHandle) {
	const { currentAccount } = useSession();
	const { pds } = getClients();
	const queryClient = useQueryClient();
	return useMutation({
		mutationFn: async () => {
			if (!currentAccount) {
				throw new Error('Not logged in');
			}

			await deleteRecord(pds!, {
				repo: currentAccount.did,
				collection: 'app.bsky.actor.status',
				rkey: 'self',
			});
		},
		onSuccess: () => {
			Toast.show(m['features.liveNow.goLive.ended']());
			handle.close();

			if (!currentAccount) {
				return;
			}

			updateProfileShadow(queryClient, currentAccount.did, {
				status: undefined,
			});
		},
		onError: (e) => {
			console.error('Failed to remove live status', e);
		},
	});
}
