import { readdir, readFile, stat } from "node:fs/promises";
import path from "node:path";
import {
	accountBaseRoot,
	accountSessionInputRoot,
	accountSessionOutputRoot,
	getCurrentAccount,
} from "@/lib/claw-auth";

export const runtime = "nodejs";
const CLAW_API_URL = process.env.CLAW_API_URL ?? "http://127.0.0.1:8765";

export async function GET(req: Request) {
	const account = await getCurrentAccount();
	if (!account)
		return Response.json({ error: "请先登录账号" }, { status: 401 });

	const url = new URL(req.url);
	const requestedPath = url.searchParams.get("path");
	const sessionId = normalizeSessionId(url.searchParams.get("session_id"));
	const onlineDocs = await readOnlineDocMap(account.id);
	if (!requestedPath && sessionId) {
		const remoteFiles = await listRemoteSessionFiles(
			account.id,
			sessionId,
			onlineDocs,
		);
		return Response.json({
			session_id: sessionId,
			input: [
				...(await listSessionFiles(account.id, sessionId, "input", onlineDocs)),
				...remoteFiles.input,
			],
			output: [
				...(await listSessionFiles(
					account.id,
					sessionId,
					"output",
					onlineDocs,
				)),
				...remoteFiles.output,
			],
		});
	}
	if (!requestedPath) {
		const latestSessionId = await findLatestSessionId(account.id);
		if (!latestSessionId) {
			return Response.json({ session_id: null, input: [], output: [] });
		}
		const remoteFiles = await listRemoteSessionFiles(
			account.id,
			latestSessionId,
			onlineDocs,
		);
		return Response.json({
			session_id: latestSessionId,
			input: [
				...(await listSessionFiles(
					account.id,
					latestSessionId,
					"input",
					onlineDocs,
				)),
				...remoteFiles.input,
			],
			output: [
				...(await listSessionFiles(
					account.id,
					latestSessionId,
					"output",
					onlineDocs,
				)),
				...remoteFiles.output,
			],
		});
	}

	if (requestedPath.startsWith("jupyter://")) {
		return downloadRemoteFile(account.id, requestedPath);
	}

	const accountRoot = path.resolve(accountBaseRoot(account.id));
	const filePath = path.resolve(requestedPath);
	if (!filePath.startsWith(`${accountRoot}${path.sep}`)) {
		return Response.json({ error: "文件不在当前账号目录内" }, { status: 403 });
	}

	try {
		const fileStat = await stat(filePath);
		if (!fileStat.isFile()) {
			return Response.json({ error: "目标不是文件" }, { status: 400 });
		}
		const bytes = await readFile(filePath);
		return new Response(bytes, {
			headers: {
				"content-type": contentTypeFor(filePath),
				"content-disposition": `attachment; filename="${encodeURIComponent(path.basename(filePath))}"`,
			},
		});
	} catch {
		return Response.json({ error: "文件不存在" }, { status: 404 });
	}
}

async function listRemoteSessionFiles(
	accountId: string,
	sessionId: string,
	onlineDocs: Record<string, OnlineDocRecord>,
) {
	try {
		const target = new URL(`${CLAW_API_URL}/api/jupyter/files`);
		target.searchParams.set("account_id", accountId);
		target.searchParams.set("session_id", sessionId);
		const response = await fetch(target, { cache: "no-store" });
		if (!response.ok) return { input: [], output: [] };
		const payload = (await response.json()) as {
			input?: RemoteSessionFile[];
			output?: RemoteSessionFile[];
		};
		return {
			input: mapRemoteFiles(payload.input, sessionId, "input", onlineDocs),
			output: mapRemoteFiles(payload.output, sessionId, "output", onlineDocs),
		};
	} catch {
		return { input: [], output: [] };
	}
}

type RemoteSessionFile = {
	name?: unknown;
	path?: unknown;
	kind?: unknown;
	size?: unknown;
	modified_at?: unknown;
};

function mapRemoteFiles(
	files: RemoteSessionFile[] | undefined,
	sessionId: string,
	kind: "input" | "output",
	onlineDocs: Record<string, OnlineDocRecord>,
) {
	if (!Array.isArray(files)) return [];
	return files
		.map((file) => {
			const remotePath =
				typeof file.path === "string" && file.path.startsWith("/")
					? file.path
					: null;
			if (!remotePath) return null;
			const name =
				typeof file.name === "string" && file.name.trim()
					? file.name.trim()
					: path.basename(remotePath);
			const uri = toJupyterFileUri(sessionId, remotePath);
			const onlineDoc = onlineDocs[uri];
			return {
				name,
				path: uri,
				kind,
				source: "jupyter",
				size: typeof file.size === "number" ? file.size : 0,
				modified_at:
					typeof file.modified_at === "string"
						? file.modified_at
						: new Date().toISOString(),
				download_url: `/api/claw/files?path=${encodeURIComponent(uri)}`,
				online_doc_url: cleanOnlineDocUrl(onlineDoc?.url) ?? null,
				online_doc_title: onlineDoc?.title ?? null,
				online_doc_kind: onlineDoc?.kind ?? null,
				online_doc_updated_at: onlineDoc?.updated_at ?? null,
			};
		})
		.filter((file): file is NonNullable<typeof file> => Boolean(file));
}

async function downloadRemoteFile(accountId: string, uri: string) {
	const parsed = parseJupyterFileUri(uri);
	if (!parsed) {
		return Response.json({ error: "无效的远端文件路径" }, { status: 400 });
	}
	const target = new URL(`${CLAW_API_URL}/api/jupyter/file`);
	target.searchParams.set("account_id", accountId);
	target.searchParams.set("session_id", parsed.sessionId);
	target.searchParams.set("path", parsed.remotePath);
	const response = await fetch(target, { cache: "no-store" });
	if (!response.ok) {
		const payload = await response.text();
		return new Response(payload, {
			status: response.status,
			headers: {
				"content-type":
					response.headers.get("content-type") ?? "application/json",
			},
		});
	}
	return new Response(response.body, {
		status: response.status,
		headers: {
			"content-type":
				response.headers.get("content-type") ?? "application/octet-stream",
			"content-disposition":
				response.headers.get("content-disposition") ??
				`attachment; filename="${encodeURIComponent(path.basename(parsed.remotePath))}"`,
		},
	});
}

function toJupyterFileUri(sessionId: string, remotePath: string) {
	return `jupyter://${sessionId}${remotePath}`;
}

function parseJupyterFileUri(uri: string) {
	const match = uri.match(/^jupyter:\/\/([^/]+)(\/.*)$/u);
	if (!match) return null;
	return {
		sessionId: match[1],
		remotePath: match[2],
	};
}

async function listSessionFiles(
	accountId: string,
	sessionId: string,
	kind: "input" | "output",
	onlineDocs: Record<string, OnlineDocRecord>,
) {
	const accountRoot = path.resolve(accountBaseRoot(accountId));
	const dir =
		kind === "input"
			? accountSessionInputRoot(accountId, sessionId)
			: accountSessionOutputRoot(accountId, sessionId);
	const resolvedDir = path.resolve(dir);
	if (!resolvedDir.startsWith(`${accountRoot}${path.sep}`)) return [];

	try {
		const entries = await readdir(resolvedDir, { withFileTypes: true });
		const files = await Promise.all(
			entries
				.filter((entry) => entry.isFile())
				.map(async (entry) => {
					const filePath = path.join(resolvedDir, entry.name);
					const fileStat = await stat(filePath);
					const onlineDoc = onlineDocs[path.resolve(filePath)];
					return {
						name: entry.name,
						path: filePath,
						kind,
						size: fileStat.size,
						modified_at: fileStat.mtime.toISOString(),
						download_url: `/api/claw/files?path=${encodeURIComponent(filePath)}`,
						online_doc_url: cleanOnlineDocUrl(onlineDoc?.url) ?? null,
						online_doc_title: onlineDoc?.title ?? null,
						online_doc_kind: onlineDoc?.kind ?? null,
						online_doc_updated_at: onlineDoc?.updated_at ?? null,
					};
				}),
		);
		return files.sort((a, b) => b.modified_at.localeCompare(a.modified_at));
	} catch {
		return [];
	}
}

function cleanOnlineDocUrl(value: unknown) {
	if (typeof value !== "string") return null;
	return value.trim().replace(/[.,;，。；、"'“”‘’]+$/u, "") || null;
}

type OnlineDocRecord = {
	url?: string;
	title?: string;
	kind?: string;
	updated_at?: number;
};

async function readOnlineDocMap(accountId: string) {
	const mapPath = path.join(
		accountBaseRoot(accountId),
		"integrations",
		"feishu",
		"online-docs.json",
	);
	try {
		const payload = JSON.parse(await readFile(mapPath, "utf8")) as {
			files?: Record<string, OnlineDocRecord>;
		};
		return payload.files ?? {};
	} catch {
		return {};
	}
}

function normalizeSessionId(value: string | null) {
	const trimmed = value?.trim();
	return trimmed || null;
}

async function findLatestSessionId(accountId: string) {
	const sessionsRoot = path.join(accountBaseRoot(accountId), "sessions");
	try {
		const entries = await readdir(sessionsRoot, { withFileTypes: true });
		const candidates = await Promise.all(
			entries
				.filter((entry) => entry.isDirectory())
				.map(async (entry) => {
					const sessionDir = path.join(sessionsRoot, entry.name);
					const sessionFile = path.join(sessionDir, "session.json");
					try {
						const sessionStat = await stat(sessionFile);
						return { id: entry.name, modifiedAt: sessionStat.mtimeMs };
					} catch {
						const dirStat = await stat(sessionDir);
						return { id: entry.name, modifiedAt: dirStat.mtimeMs };
					}
				}),
		);
		candidates.sort((a, b) => b.modifiedAt - a.modifiedAt);
		return candidates.at(0)?.id ?? null;
	} catch {
		return null;
	}
}

function contentTypeFor(filePath: string) {
	const ext = path.extname(filePath).toLowerCase();
	if (ext === ".json") return "application/json; charset=utf-8";
	if (ext === ".jsonl" || ext === ".txt" || ext === ".md" || ext === ".csv") {
		return "text/plain; charset=utf-8";
	}
	return "application/octet-stream";
}
