import TurndownService from 'turndown';
import { URLSearchParams } from 'url';
import vscode from 'vscode';
import { MergeRequestModel } from './coding/mergeRequestModel';
import {
	_getMergeRequestInfo,
	_getMRComments,
	_postLineNote,
	DescribeDifferentBetween2Commits,
	getCurrentUserInfo,
} from './codingApi';
import { CODING_MR_SCHEME, EmptyUserAvatar } from './common';
import { getDiffLineNumber, isHunkLine, md5Hash } from './common/utils';
import { IDiffComment, IFileDiffParam } from './privateCodingApiType';

let commentIdx = 0;

export class ReviewComment implements vscode.Comment {
	id: number;
	label: string | undefined;
	constructor(
		public body: string | vscode.MarkdownString,
		public mode: vscode.CommentMode,
		public author: vscode.CommentAuthorInformation,
		public parent?: vscode.CommentThread,
		public contextValue?: string,
		public commentId?: number,
	) {
		this.id = commentId ?? ++commentIdx;
	}
}

const diffFileCache = new Map<string, any>();

const commentThreadsCache = new Map<string, vscode.CommentThread[]>();

const commentControllersCache = new Map<string, vscode.CommentController>();

export async function replyNote(reply: vscode.CommentReply) {
	const params = new URLSearchParams(JSON.parse(reply.thread.uri.query));
	const isRight = params.get('isBase') === `true`;
	const ident = `${params.get(`mergeId`)}/${params.get(`fileName`)}`;
	const diffFile = diffFileCache.get(ident);

	if (!diffFile) {
		throw new Error('diffFile cache not found: ' + ident);
	}

	const commitId = /* isRight ? params.get('rightSha') : params.get('leftSha') */ params.get('commit');
	const content = reply.text;
	const noteable_type = `MergeRequestBean`;
	const change_type = isRight ? 1 : 2;
	const line = reply.thread.range.start.line + 1;
	const path = params.get(`fileName`) || ``;
	// FIXME 这个查找逻辑不太严谨
	const targetPos = diffFile.DifferentLines.reverse().find(i => {
		return i[isRight ? `RightNo` : `LeftNo`] === line;
	});
	const position = targetPos?.Index ?? 0;

	try {
		const {
			data: { merge_request },
		} = await _getMergeRequestInfo({ mergeId: params.get('mergeId') || '' });

		const resp = await _postLineNote({
			noteable_id: merge_request.id, // mr 的长id
			commitId: commitId ?? ``,
			content,
			noteable_type,
			change_type,
			line,
			path,
			position,
			// anchor: `diff-${diffFile.pathMD5}`,
			anchor: `diff-${md5Hash(path)}`,
		});

		// const curUser = context.workspaceState.get<AuthenticationSession>(`session`);
		const { data: currentUser } = await getCurrentUserInfo();
		const commentAuthor: vscode.CommentAuthorInformation = currentUser
			? {
					name: `${currentUser.name} (${currentUser.global_key})`,
					iconPath: vscode.Uri.parse(currentUser.avatar, false),
			  }
			: {
					name: `vscode user`,
					iconPath: vscode.Uri.parse(EmptyUserAvatar, false),
			  };
		const thread = reply.thread;
		thread.contextValue = `editable`;
		const newComment = new ReviewComment(
			reply.text,
			vscode.CommentMode.Preview,
			commentAuthor,
			thread,
			thread.comments.length ? 'canDelete' : undefined,
			resp.data.id,
		);

		thread.comments = [...thread.comments, newComment];
	} catch (e) {}

	const threadId = `${params.get('mergeId')}/${path}`;
	commentThreadsCache[threadId].push(reply.thread);
}

export const makeCommentRangeProvider = (
	// diffFileData: IDiffFileData,
	mrModel: MergeRequestModel,
) => async (document: vscode.TextDocument, token: vscode.CancellationToken) => {
	if (document.uri.scheme !== CODING_MR_SCHEME) {
		return [];
	}

	try {
		const params = new URLSearchParams(JSON.parse(document.uri.query));
		const mergeId = params.get('mergeId') || ``;
		let param: IFileDiffParam = {
			path: params.get('fileName') ?? ``,
			base: params.get('baseCommit') ?? ``,
			compare: params.get('headCommit') ?? ``,
			mergeRequestId: mergeId ?? ``,
		};
		const fileIdent = `${mergeId}/${params.get(`fileName`)}`;

		// const { data } = await _fetchFileDiffs(param);
		const { DiffFileInfo } = await DescribeDifferentBetween2Commits({
			depotId: mrModel.depot.id,
			source: param.base,
			target: param.compare,
			path: param.path,
		});

		diffFileCache.set(fileIdent, DiffFileInfo);
		// const { diffLines } = data;
		const { DifferentLines } = DiffFileInfo;

		const ret = DifferentLines.reduce((result, i) => {
			const isHunk = isHunkLine(i.Text);
			if (!isHunk) {
				return result;
			}

			const [left, right] = getDiffLineNumber(i.Text);
			const [start, end] = params.get('isBase') === `true` ? right : left;
			if (start > 0) {
				result.push(new vscode.Range(start - 1, 0, end, 0));
			}
			return result;
		}, [] as vscode.Range[]);
		return ret;
	} catch (e) {
		console.error('fetch diff lines failed.');
		return [];
	}
};

export const showDiffComment = async (parentUri: vscode.Uri, headUri: vscode.Uri, mrModel: MergeRequestModel) => {
	const mergeId = JSON.parse(parentUri.query).mergeId;

	if (!mergeId) return;

	const cacheId = `${mergeId}-${parentUri.path}`;
	const tdService = new TurndownService();
	let commentController = commentControllersCache.get(mergeId);

	if (!commentController) {
		commentController = vscode.comments.createCommentController(
			`mr-${mergeId}`,
			`mr-${mergeId}-comment-controller`,
		);

		commentController.commentingRangeProvider = {
			provideCommentingRanges: makeCommentRangeProvider(mrModel /* diffFileData */),
		};
		commentControllersCache.set(mergeId, commentController);
	}

	if (commentThreadsCache.get(cacheId)) return;

	try {
		const commentResp = await _getMRComments({ mergeId });

		const validComments = (commentResp.data as IDiffComment[][])
			.filter(i => {
				const first = i[0];
				return !first.outdated && `/${first.path}` === parentUri.path;
			}, [])
			.reduce((ret, i) => {
				const ident = `${i[0].change_type === 1 ? 'right' : 'left'}-${i[0].position}-${i[0].line}`;
				ret[ident] = (ret[ident] ?? []).concat(i);
				return ret;
			}, {} as { [key: string]: IDiffComment[] });

		Object.values(validComments).forEach(i => {
			const root = i[0];
			const isRight = root.change_type === 1;

			const rootLine = root.diffFile.diffLines[root.diffFile.diffLines.length - 1];
			const lineNum = isRight ? rootLine.rightNo - 1 : rootLine.leftNo - 1;
			const range = new vscode.Range(lineNum, 0, lineNum, 0);

			const commentList: vscode.Comment[] = i
				.sort((a, b) => a.created_at - b.created_at)
				.map(c => {
					const body = new vscode.MarkdownString(tdService.turndown(c.content));
					body.isTrusted = true;
					const comment = new ReviewComment(
						body,
						vscode.CommentMode.Preview,
						{
							name: `${c.author.name}(${c.author.global_key})`,
							iconPath: vscode.Uri.parse(c.author.avatar, false),
						},
						undefined,
						'canDelete',
						c.id,
					);

					return comment;
				});

			const commentThread = commentController!.createCommentThread(isRight ? headUri : parentUri, range, []);
			commentThread.comments = commentList;
			commentThread.collapsibleState = vscode.CommentThreadCollapsibleState.Expanded;

			commentThreadsCache.set(cacheId, (commentThreadsCache[mergeId] ?? []).concat(commentThread));
		});
	} finally {
	}
};
