import type { IssueComment } from '../../lib/api/issues';
import type { SearchParamValue } from '../../lib/repo-utils';

export type IssueStateFilter = 'open' | 'closed';

export interface IssueFilter {
	state: IssueStateFilter;
	author?: string;
	query?: string;
}

const searchParamString = (value: SearchParamValue): string => Array.isArray(value) ? value[0] ?? '' : value ?? '';

export const parseIssueFilter = (value: SearchParamValue): IssueFilter => {
	const filter: IssueFilter = { state: 'open' };
	const tokens = searchParamString(value).split(/\s+/).filter(Boolean);
	const queryTokens: string[] = [];

	for (const token of tokens) {
		if (token === 'state:closed') {
			filter.state = 'closed';
			continue;
		}

		if (token === 'state:open') {
			filter.state = 'open';
			continue;
		}

		if (token.startsWith('author:')) {
			const author = token.slice('author:'.length).trim();
			if (author) {
				filter.author = author;
			}
			continue;
		}

		queryTokens.push(token);
	}

	const query = queryTokens.join(' ').trim();
	if (query) {
		filter.query = query;
	}

	return filter;
};

export const buildIssueFilterQuery = (filter: IssueFilter): string =>
	[
		filter.query,
		`state:${filter.state}`,
		filter.author ? `author:${filter.author}` : undefined,
	].filter(Boolean).join(' ');

export type IssueCommentThread = {
	item: IssueComment;
	replies: IssueComment[];
};

export const buildIssueCommentThreads = (comments: IssueComment[]): IssueCommentThread[] => {
	const byUri = new Map(comments.map((comment) => [comment.uri, comment]));
	const topLevel: IssueComment[] = [];
	const repliesByRoot = new Map<string, IssueComment[]>();

	const findRoot = (comment: IssueComment): IssueComment | null => {
		let current = comment;
		const visited = new Set<string>([comment.uri]);

		while (current.value.replyTo) {
			const parent = byUri.get(current.value.replyTo);
			if (!parent || visited.has(parent.uri)) {
				return current === comment ? null : current;
			}

			current = parent;
			visited.add(current.uri);
		}

		return current;
	};

	for (const comment of comments) {
		const root = findRoot(comment);
		if (!root || root.uri === comment.uri) {
			topLevel.push(comment);
			continue;
		}

		repliesByRoot.set(root.uri, [...(repliesByRoot.get(root.uri) ?? []), comment]);
	}

	return topLevel.map((item) => ({
		item,
		replies: repliesByRoot.get(item.uri) ?? [],
	}));
};
