import * as path from "node:path";
import * as vscode from "vscode";
import type { SfChangeTracker } from "./change-tracker.js";

const SF_ORIGINAL_SCHEME = "sf-original";

/**
 * Source Control provider that shows files modified by the SF agent
 * in a dedicated "SF Agent" section of the Source Control panel.
 * Supports QuickDiff to show before/after diffs, and accept/discard per-file.
 */
export class SfScmProvider implements vscode.Disposable {
	private readonly scm: vscode.SourceControl;
	private readonly changesGroup: vscode.SourceControlResourceGroup;
	private readonly contentProvider: SfOriginalContentProvider;
	private disposables: vscode.Disposable[] = [];

	constructor(
		private readonly tracker: SfChangeTracker,
		private readonly workspaceRoot: string,
	) {
		// Register content provider for original file contents
		this.contentProvider = new SfOriginalContentProvider(tracker);
		this.disposables.push(
			vscode.workspace.registerTextDocumentContentProvider(
				SF_ORIGINAL_SCHEME,
				this.contentProvider,
			),
		);

		// Create source control instance
		this.scm = vscode.scm.createSourceControl(
			"sf",
			"SF Agent",
			vscode.Uri.file(workspaceRoot),
		);
		this.scm.quickDiffProvider = {
			provideOriginalResource: (uri: vscode.Uri): vscode.Uri | undefined => {
				const filePath = uri.fsPath;
				if (this.tracker.getOriginal(filePath) !== undefined) {
					return uri.with({ scheme: SF_ORIGINAL_SCHEME });
				}
				return undefined;
			},
		};
		this.scm.inputBox.placeholder = "Describe changes to accept...";
		this.scm.acceptInputCommand = {
			command: "sf.acceptAllChanges",
			title: "Accept All",
		};
		this.scm.count = 0;
		this.disposables.push(this.scm);

		// Create resource group
		this.changesGroup = this.scm.createResourceGroup(
			"changes",
			"Agent Changes",
		);
		this.changesGroup.hideWhenEmpty = true;
		this.disposables.push(this.changesGroup);

		// Listen for change tracker updates
		this.disposables.push(tracker.onDidChange(() => this.refresh()));

		this.refresh();
	}

	private refresh(): void {
		const files = this.tracker.modifiedFiles;
		this.changesGroup.resourceStates = files.map((filePath) => {
			const uri = vscode.Uri.file(filePath);
			const fileName = path.basename(filePath);
			const _relativePath = path.relative(this.workspaceRoot, filePath);

			const state: vscode.SourceControlResourceState = {
				resourceUri: uri,
				decorations: {
					strikeThrough: false,
					tooltip: `Modified by SF Agent`,
					light: { iconPath: new vscode.ThemeIcon("edit") },
					dark: { iconPath: new vscode.ThemeIcon("edit") },
				},
				command: {
					command: "vscode.diff",
					title: "Show Changes",
					arguments: [
						uri.with({ scheme: SF_ORIGINAL_SCHEME }),
						uri,
						`${fileName} (SF Agent Changes)`,
					],
				},
			};
			return state;
		});
		this.scm.count = files.length;
	}

	dispose(): void {
		for (const d of this.disposables) {
			d.dispose();
		}
	}
}

/**
 * TextDocumentContentProvider that serves the original (pre-agent) content
 * of files via the `sf-original:` URI scheme.
 */
class SfOriginalContentProvider implements vscode.TextDocumentContentProvider {
	private readonly _onDidChange = new vscode.EventEmitter<vscode.Uri>();
	readonly onDidChange = this._onDidChange.event;

	constructor(private readonly tracker: SfChangeTracker) {
		tracker.onDidChange((paths) => {
			for (const p of paths) {
				this._onDidChange.fire(
					vscode.Uri.file(p).with({ scheme: SF_ORIGINAL_SCHEME }),
				);
			}
		});
	}

	provideTextDocumentContent(uri: vscode.Uri): string {
		const filePath = uri.with({ scheme: "file" }).fsPath;
		return this.tracker.getOriginal(filePath) ?? "";
	}
}
