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

export type PullStateFilter = 'open' | 'closed' | 'merged';
export type PullSourceMode = 'branch' | 'fork' | 'patch';

export interface PullFilter {
	state: PullStateFilter;
	author?: string;
	query?: string;
}

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

export const parsePullFilter = (value: SearchParamValue): PullFilter => {
	const filter: PullFilter = { 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:merged') {
			filter.state = 'merged';
			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 parsePullStateFilter = (value: SearchParamValue): PullStateFilter => parsePullFilter(value).state;

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

export const parsePastedPatchPrefill = (patch: string): { title: string; body: string } => {
	const subject = /^Subject:\s*(?:\[[^\]]+\]\s*)?(.*)$/im.exec(patch)?.[1]?.trim() ?? '';
	const headerEnd = patch.indexOf('\n---');
	const bodyStart = patch.search(/^Subject:.*$/im);

	if (!subject || bodyStart === -1 || headerEnd === -1 || headerEnd <= bodyStart) {
		return { title: subject, body: '' };
	}

	const afterSubject = patch.slice(bodyStart).replace(/^Subject:.*(?:\r?\n)?/i, '');
	const separator = afterSubject.indexOf('\n---');
	const body = separator === -1 ? '' : afterSubject.slice(0, separator).trim();

	return { title: subject, body };
};

import type { PullComment } from '../../lib/api/pulls';

export type PRCommentThread = {
	item: PullComment;
	replies: PullComment[];
};

export const getRoundIndexForComment = (commentCreatedAt: string, rounds: Array<{ createdAt: string }>): number => {
	if (rounds.length <= 1) return 0;
	const commentTime = new Date(commentCreatedAt).getTime();

	for (let i = 1; i < rounds.length; i++) {
		const nextRoundTime = new Date(rounds[i].createdAt).getTime();
		if (commentTime < nextRoundTime) {
			return i - 1;
		}
	}
	return rounds.length - 1;
};

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

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

		while ((current.value as any).replyTo) {
			const replyToRef = (current.value as any).replyTo;
			const parentUri = typeof replyToRef === 'string' ? replyToRef : (replyToRef.uri || replyToRef.URI || replyToRef.Uri);
			if (!parentUri) break;

			const parent = byUri.get(parentUri);
			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) ?? [],
	}));
};
