import { TreeItem, TreeItemCollapsibleState, Uri } from 'vscode';
import type { GitReflog } from '@gitlens/git/models/reflog.js';
import { trace } from '@gitlens/utils/decorators/log.js';
import type { GitUri } from '../../git/gitUri.js';
import type { GlRepository } from '../../git/models/repository.js';
import type { RepositoriesView } from '../repositoriesView.js';
import type { WorkspacesView } from '../workspacesView.js';
import { CacheableChildrenViewNode } from './abstract/cacheableChildrenViewNode.js';
import type { PageableViewNode, ViewNode } from './abstract/viewNode.js';
import { ContextValues, getViewNodeId } from './abstract/viewNode.js';
import { LoadMoreNode, MessageNode } from './common.js';
import { ReflogRecordNode } from './reflogRecordNode.js';

export class ReflogNode
	extends CacheableChildrenViewNode<'reflog', RepositoriesView | WorkspacesView>
	implements PageableViewNode
{
	limit: number | undefined;

	constructor(
		uri: GitUri,
		view: RepositoriesView | WorkspacesView,
		parent: ViewNode,
		public readonly repo: GlRepository,
	) {
		super('reflog', uri, view, parent);

		this.updateContext({ repository: repo });
		this._uniqueId = getViewNodeId(this.type, this.context);
		this.limit = this.view.getNodeLastKnownLimit(this);
	}

	override get id(): string {
		return this._uniqueId;
	}

	async getChildren(): Promise<ViewNode[]> {
		if (this.children === undefined) {
			const children = [];

			const reflog = await this.getReflog();
			if (reflog === undefined || reflog.records.length === 0) {
				return [new MessageNode(this.view, this, 'No activity could be found.')];
			}

			children.push(...reflog.records.map(r => new ReflogRecordNode(this.view, this, r)));

			if (reflog.hasMore) {
				children.push(new LoadMoreNode(this.view, this, children.at(-1)!));
			}

			this.children = children;
		}
		return this.children;
	}

	getTreeItem(): TreeItem {
		const item = new TreeItem('Incoming Activity', TreeItemCollapsibleState.Collapsed);
		item.id = this.id;
		item.contextValue = ContextValues.Reflog;
		item.description = 'experimental';
		item.iconPath = {
			dark: Uri.file(this.view.container.context.asAbsolutePath('images/dark/icon-activity.svg')),
			light: Uri.file(this.view.container.context.asAbsolutePath('images/light/icon-activity.svg')),
		};

		return item;
	}

	@trace()
	override refresh(reset?: boolean): void | { cancel: boolean } | Promise<void | { cancel: boolean }> {
		if (reset) {
			this._reflog = undefined;
		}
		return super.refresh(true);
	}

	private _reflog: GitReflog | undefined;
	private async getReflog() {
		if (this._reflog === undefined) {
			this._reflog = await this.repo.git.commits.getIncomingActivity?.({
				all: true,
				limit: this.limit ?? this.view.config.defaultItemLimit,
			});
		}

		return this._reflog;
	}

	get hasMore(): boolean {
		return this._reflog?.hasMore ?? true;
	}

	async loadMore(limit?: number): Promise<void> {
		let reflog = await this.getReflog();
		if (!reflog?.hasMore) return;

		reflog = await reflog.more?.(limit ?? this.view.config.pageItemLimit);
		if (this._reflog === reflog) return;

		this._reflog = reflog;
		this.limit = reflog?.count;
		void this.triggerChange(false);
	}
}
