import type { Did, ResourceUri } from '@atcute/lexicons/syntax';
import {
	ShTangledActorProfile,
	ShTangledLabelDefinition,
	ShTangledRepo,
	ShTangledRepoIssue,
	ShTangledRepoIssueComment,
	ShTangledRepoPull,
	ShTangledRepoPullComment,
	ShTangledString,
} from '@atcute/tangled';

import {
	appviewJson,
	type AppviewSearchResponse,
} from './appview';
import { appviewRecordToHydrated } from './hydration';
import type { HydratedRecord } from './records';

export type SearchCollection =
	| 'sh.tangled.actor.profile'
	| 'sh.tangled.repo'
	| 'sh.tangled.repo.issue'
	| 'sh.tangled.repo.issue.comment'
	| 'sh.tangled.repo.pull'
	| 'sh.tangled.repo.pull.comment'
	| 'sh.tangled.feed.comment'
	| 'sh.tangled.string'
	| 'sh.tangled.label.definition';

export interface ShTangledFeedComment {
	$type: 'sh.tangled.feed.comment';
	subject: {
		uri: ResourceUri;
		cid: string;
	};
	body: {
		$type: 'sh.tangled.markup.markdown';
		text: string;
	};
	createdAt: string;
	replyTo?: {
		uri: ResourceUri;
		cid: string;
	};
}

export type SearchRecordValue =
	| ShTangledActorProfile.Main
	| ShTangledRepo.Main
	| ShTangledRepoIssue.Main
	| ShTangledRepoIssueComment.Main
	| ShTangledRepoPull.Main
	| ShTangledRepoPullComment.Main
	| ShTangledString.Main
	| ShTangledLabelDefinition.Main
	| ShTangledFeedComment;

export interface SearchHit extends HydratedRecord<SearchRecordValue> {
	nsid: SearchCollection;
	score: number;
}

export interface SearchResponse {
	hits: SearchHit[];
	cursor?: string;
}

export interface SearchRecordsOptions {
	q: string;
	nsid?: SearchCollection;
	author?: Did;
	repo?: Did;
	since?: string;
	until?: string;
	cursor?: string;
	limit?: number;
}

export const searchRecords = async (options: SearchRecordsOptions): Promise<SearchResponse> => {
	const q = options.q.trim();
	if (!q) {
		return { hits: [] };
	}

	const response = await appviewJson<AppviewSearchResponse<SearchRecordValue>>(
		'sh.tangled.search.query',
		{
			q,
			nsid: options.nsid,
			author: options.author,
			repo: options.repo,
			since: options.since,
			until: options.until,
			cursor: options.cursor,
			limit: options.limit,
		},
	);
	const hits = await Promise.all(
		response.hits.map(async (hit) => ({
			...(await appviewRecordToHydrated(hit)),
			nsid: hit.nsid as SearchCollection,
			score: hit.score,
		})),
	);

	return {
		hits,
		cursor: response.cursor ?? undefined,
	};
};
