import { ok } from '@atcute/client';
import type { Did, Nsid, ResourceUri } from '@atcute/lexicons/syntax';

import {
	appviewBulkRecords,
	appviewFallback,
	type AppviewRecordView,
} from './appview';
import { getRpc } from './client';
import {
	type BacklinkRecordRef,
	type HydratedRecord,
	parseAtUri,
} from './records';
import { resolveActor, resolveActorForDisplay } from './identity';

export const appviewRecordToHydrated = async <T>(record: AppviewRecordView<T>): Promise<HydratedRecord<T>> => {
	const parsed = parseAtUri(record.uri);
	const author = await resolveActorForDisplay(parsed.did);

	return {
		author,
		collection: parsed.collection,
		rkey: parsed.rkey,
		uri: record.uri,
		cid: record.cid ?? undefined,
		value: record.value,
	};
};

export const hydrateRecord = async <T>(
	ref: BacklinkRecordRef | { did: Did; collection: Nsid; rkey: string },
): Promise<HydratedRecord<T>> => {
	const actor = await resolveActor(ref.did);
	const record = await ok(
		getRpc(actor.pds).get('com.atproto.repo.getRecord', {
			params: {
				repo: ref.did,
				collection: ref.collection,
				rkey: ref.rkey,
			},
		}),
	);

	return {
		author: actor,
		collection: ref.collection,
		rkey: ref.rkey,
		uri: record.uri,
		cid: record.cid,
		value: record.value as T,
	};
};

export const fetchRecordValue = async <T>(
	ref: BacklinkRecordRef | { did: Did; collection: Nsid; rkey: string },
): Promise<T> => {
	const actor = await resolveActor(ref.did);
	const record = await ok(
		getRpc(actor.pds).get('com.atproto.repo.getRecord', {
			params: {
				repo: ref.did,
				collection: ref.collection,
				rkey: ref.rkey,
			},
		}),
	);

	return record.value as T;
};

export const hydrateBacklinks = async <T>(refs: BacklinkRecordRef[]): Promise<Array<HydratedRecord<T>>> =>
	Promise.all(refs.map((ref) => hydrateRecord<T>(ref)));

const backlinkRefToUri = (ref: BacklinkRecordRef): ResourceUri =>
	`at://${ref.did}/${ref.collection}/${ref.rkey}` as ResourceUri;

export const hydrateBacklinksFromAppview = async <T>(
	refs: BacklinkRecordRef[],
	nsid: string,
	paramName: string,
): Promise<Array<HydratedRecord<T>>> =>
	appviewFallback(
		async () => {
			const records = await appviewBulkRecords<T>(
				nsid,
				paramName,
				refs.map(backlinkRefToUri),
			);
			return Promise.all(records.map((record) => appviewRecordToHydrated(record)));
		},
		() => hydrateBacklinks<T>(refs),
	);
