import type { BobbinContext, XrpcRequestInit } from "./client";
import { createBobbinClient } from "./client";
import { jsonGet, rawGet } from "./_request";
import type * as LegacyTree from "./lexicons/types/sh/tangled/git/temp/getTree";
import type * as Tree from "./lexicons/types/sh/tangled/repo/tree";
import { treeEntryKind, type BranchesResponse, type LogResponse, type TagsResponse } from "./repo";

const TREE_NSID = "sh.tangled.git.temp.getTree";
const BLOB_NSID = "sh.tangled.git.temp.getBlob";
const LOG_NSID = "sh.tangled.git.temp.listCommits";
const BRANCHES_NSID = "sh.tangled.git.temp.listBranches";
const TAGS_NSID = "sh.tangled.git.temp.listTags";
const LANGUAGES_NSID = "sh.tangled.git.temp.listLanguages";

export interface KnotMirrorLanguagesResponse {
	languages?: { name: string; size: number }[];
	total?: number;
	ref?: string;
}

export const createKnotMirrorClient = (serviceUrl: string, fetch: typeof globalThis.fetch) =>
	createBobbinClient({ serviceUrl, fetch });

const normalizeSignature = (
	signature: LegacyTree.Signature | undefined
): Tree.Signature | undefined =>
	signature ? { name: signature.name, email: signature.email, when: signature.when } : undefined;

const normalizeCommit = (commit: LegacyTree.LastCommit | undefined): Tree.LastCommit | undefined =>
	commit
		? {
				hash: commit.hash,
				message: commit.message,
				when: commit.when,
				author: normalizeSignature(commit.author)
			}
		: undefined;

const normalizeTree = (tree: LegacyTree.$output): Tree.$output => ({
	ref: tree.ref,
	parent: tree.parent,
	dotdot: tree.dotdot,
	lastCommit: normalizeCommit(tree.lastCommit),
	files: tree.files.map((entry) => ({
		name: entry.name,
		mode: entry.mode,
		size: entry.size,
		last_commit: normalizeCommit(entry.last_commit)
	}))
});

const isReadmeFile = (name: string, mode: string): boolean =>
	/^readme(?:\.[^.]+)?$/i.test(name) && treeEntryKind(mode) === "file";

const pathInTree = (path: string | undefined, name: string): string =>
	path ? `${path}/${name}` : name;

export const tree = async (
	ctx: BobbinContext,
	params: { repo: string; ref: string; path?: string },
	init?: XrpcRequestInit
): Promise<Tree.$output> => {
	const response = await jsonGet<LegacyTree.$output>(ctx, TREE_NSID, params, init);
	const result = normalizeTree(response);
	const readme = response.files.find((entry) => isReadmeFile(entry.name, entry.mode));
	if (!readme) return result;

	try {
		const blob = await rawGet(
			ctx,
			BLOB_NSID,
			{ repo: params.repo, ref: params.ref, path: pathInTree(params.path, readme.name) },
			init
		);
		return { ...result, readme: { filename: readme.name, contents: await blob.text() } };
	} catch {
		return result;
	}
};

export const log = (
	ctx: BobbinContext,
	params: { repo: string; ref: string; limit: number },
	init?: XrpcRequestInit
) => jsonGet<LogResponse>(ctx, LOG_NSID, params, init);

export const branches = (
	ctx: BobbinContext,
	params: { repo: string; limit: number },
	init?: XrpcRequestInit
) => jsonGet<BranchesResponse>(ctx, BRANCHES_NSID, params, init);

export const tags = (
	ctx: BobbinContext,
	params: { repo: string; limit: number },
	init?: XrpcRequestInit
) => jsonGet<TagsResponse>(ctx, TAGS_NSID, params, init);

export const languages = (
	ctx: BobbinContext,
	params: { repo: string; ref: string },
	init?: XrpcRequestInit
) => jsonGet<KnotMirrorLanguagesResponse>(ctx, LANGUAGES_NSID, params, init);
