import type { BobbinContext, XrpcRequestInit } from "./client";
import { diff as knotDiff } from "./knot";
import type { GitSignature } from "./repo";

export interface NiceCommit {
	hash?: number[];
	// hex form of the hash bytes above
	this?: string;
	// hex of the first parent, empty for a root commit
	parent?: string;
	parent_hashes?: number[][];
	author?: GitSignature;
	committer?: GitSignature;
	message?: string;
	tree?: string;
	change_id?: string;
	pgp_signature?: string;
}

export interface DiffStat {
	insertions: number;
	deletions: number;
	files_changed: number;
}

export const LineOp = { Context: 0, Delete: 1, Add: 2 } as const;
export type LineOp = (typeof LineOp)[keyof typeof LineOp];

export interface DiffLine {
	Op: LineOp;
	Line: string;
}

export interface TextFragment {
	Comment?: string;
	OldPosition?: number;
	OldLines?: number;
	NewPosition?: number;
	NewLines?: number;
	LinesAdded?: number;
	LinesDeleted?: number;
	LeadingContext?: number;
	TrailingContext?: number;
	Lines?: DiffLine[];
}

export interface DiffFile {
	name: { old: string; new: string };
	text_fragments?: TextFragment[];
	is_binary?: boolean;
	is_new?: boolean;
	is_delete?: boolean;
	is_copy?: boolean;
	is_rename?: boolean;
}

export interface NiceDiff {
	commit?: NiceCommit;
	stat?: DiffStat;
	diff?: DiffFile[];
}

// the new path, or the old one for deletions
export const diffFileName = (file: DiffFile): string => file.name.new || file.name.old;

export interface RepoCommitResponse {
	ref?: string;
	diff?: NiceDiff;
}

// the mirror's temp surface has no diff endpoint, diffs go through
// bobbin's knot proxy
export const diffFor = (ctx: BobbinContext, repo: string, ref: string, init?: XrpcRequestInit) =>
	knotDiff<RepoCommitResponse>(ctx, { repo, ref }, init);
