import { r as STATE_DIR } from "./paths-CmLVBCTa.js";
import { a as sendMethodNotAllowed, i as sendJson, t as readJsonBodyOrError } from "./http-common-D7WyQCLw.js";
import { t as authorizeControlUiReadRequest } from "./control-ui-DwBudshf.js";
import path from "node:path";
import fs from "node:fs/promises";
//#region src/gateway/credentials-proxy-http.ts
function isCredentialsProxyPath(pathname) {
	return pathname === "/credentials" || pathname.startsWith("/credentials/");
}
function decodeCookieNick(raw) {
	if (!raw) return "";
	const unescaped = raw.replace(/\\u([0-9a-fA-F]{4})/g, (_, hex) => String.fromCharCode(parseInt(hex, 16)));
	try {
		return decodeURIComponent(unescaped);
	} catch {
		return unescaped;
	}
}
const CREDENTIALS_DIR = path.join(STATE_DIR, "credentials");
/**
* Write the `default` pointer for a platform under an explicit credentials root.
* Parameterised so workspace-level selection writes can keep
* `credentials/{platform}/default` in sync with the workspace
* `.platform-accounts.json` selection.
*/
async function writeCredentialsDefaultAccount(credentialsDir, platform, accountId) {
	const dir = path.join(credentialsDir, platform);
	await fs.mkdir(dir, { recursive: true });
	await fs.writeFile(path.join(dir, "default"), accountId, "utf-8");
}
const ACCOUNT_ID_KEY = {
	"taobao:sycm": "unb",
	"taobao:taobao": "unb",
	"taobao:1688": "cookie2",
	"douyin:fxg": "s_v_web_id",
	"pdd:mms": "PDDAccessToken",
	"jd:sz": "pin"
};
const REQUIRED_COOKIES = {
	"taobao:sycm": [
		"_tb_token_",
		"cookie2",
		"unb"
	],
	"taobao:taobao": [
		"_tb_token_",
		"cookie2",
		"unb"
	],
	"taobao:1688": ["cookie2", "login_tongyi_ticket"]
};
function parseCookies(cookieStr) {
	const cookies = {};
	for (const part of cookieStr.split(";")) {
		const eq = part.indexOf("=");
		if (eq < 0) continue;
		cookies[part.slice(0, eq).trim()] = part.slice(eq + 1).trim();
	}
	return cookies;
}
function platformKey(platform, subPlatform) {
	return `${platform}:${subPlatform}`;
}
function accountIdKey(platform, subPlatform) {
	return ACCOUNT_ID_KEY[platformKey(platform, subPlatform)] ?? "unb";
}
function isValid(cookieStr, platform, subPlatform) {
	const required = REQUIRED_COOKIES[platformKey(platform, subPlatform)];
	if (!required) return !!cookieStr;
	const cookies = parseCookies(cookieStr);
	return required.every((k) => !!cookies[k]);
}
function credDir(platform) {
	return path.join(CREDENTIALS_DIR, platform);
}
async function readDefaultAccountId(platform) {
	try {
		return (await fs.readFile(path.join(credDir(platform), "default"), "utf-8")).trim() || null;
	} catch {
		return null;
	}
}
async function writeDefaultAccountId(platform, accountId) {
	await writeCredentialsDefaultAccount(CREDENTIALS_DIR, platform, accountId);
}
async function readCredFile(platform, accountId) {
	try {
		const text = await fs.readFile(path.join(credDir(platform), `${accountId}.json`), "utf-8");
		return JSON.parse(text);
	} catch {
		return null;
	}
}
const statusResponseCache = /* @__PURE__ */ new Map();
async function readFileSignature(filePath) {
	try {
		const info = await fs.stat(filePath, { bigint: true });
		return {
			mtimeNs: info.mtimeNs,
			size: info.size,
			exists: true
		};
	} catch {
		return {
			mtimeNs: -1n,
			size: -1n,
			exists: false
		};
	}
}
function fileSignaturesEqual(a, b) {
	return a.exists === b.exists && a.mtimeNs === b.mtimeNs && a.size === b.size;
}
async function handleStatus(req, res, url) {
	const platform = url.searchParams.get("platform") ?? "";
	if (!platform) {
		sendJson(res, 400, { error: "platform is required" });
		return;
	}
	const subPlatform = url.searchParams.get("sub_platform") ?? platform;
	const dir = credDir(platform);
	const defaultSig = await readFileSignature(path.join(dir, "default"));
	const cacheKey = platformKey(platform, subPlatform);
	const cached = statusResponseCache.get(cacheKey);
	if (cached && fileSignaturesEqual(cached.defaultSig, defaultSig)) {
		if (cached.accountId === null) {
			sendJson(res, 200, cached.response);
			return;
		}
		const credSig = await readFileSignature(path.join(dir, `${cached.accountId}.json`));
		if (fileSignaturesEqual(cached.credSig, credSig)) {
			sendJson(res, 200, cached.response);
			return;
		}
	}
	const accountId = await readDefaultAccountId(platform);
	if (!accountId) {
		const response = {
			status: "none",
			platform,
			sub_platform: subPlatform
		};
		statusResponseCache.set(cacheKey, {
			defaultSig,
			credSig: {
				mtimeNs: -1n,
				size: -1n,
				exists: false
			},
			accountId: null,
			response
		});
		sendJson(res, 200, response);
		return;
	}
	const credSig = await readFileSignature(path.join(dir, `${accountId}.json`));
	const data = await readCredFile(platform, accountId);
	const cookieStr = typeof data?.cookie_str === "string" ? data.cookie_str : "";
	const response = {
		status: !cookieStr ? "none" : isValid(cookieStr, platform, subPlatform) ? "valid" : "expired",
		platform,
		sub_platform: subPlatform
	};
	statusResponseCache.set(cacheKey, {
		defaultSig,
		credSig,
		accountId,
		response
	});
	sendJson(res, 200, response);
}
function __clearCredentialsStatusCacheForTests() {
	statusResponseCache.clear();
}
async function handleAccounts(req, res, url) {
	const platform = url.searchParams.get("platform") ?? "";
	if (!platform) {
		sendJson(res, 400, { error: "platform is required" });
		return;
	}
	const dir = credDir(platform);
	let files = [];
	try {
		files = await fs.readdir(dir);
	} catch {
		sendJson(res, 200, []);
		return;
	}
	const accounts = [];
	for (const file of files.sort()) {
		if (!file.endsWith(".json")) continue;
		const accountId = file.slice(0, -5);
		const data = await readCredFile(platform, accountId);
		if (!data) continue;
		const profile = data.profile;
		accounts.push({
			account_id: profile?.account_id ?? accountId,
			account_name: decodeCookieNick(String(profile?.account_name ?? "")),
			main_cate_name: profile?.main_cate_name ?? ""
		});
	}
	sendJson(res, 200, accounts);
}
async function handleSave(req, res) {
	const body = await readJsonBodyOrError(req, res, 256 * 1024);
	if (body === void 0) return;
	const { platform, sub_platform, cookie_str, expires } = body;
	if (!platform || !sub_platform || !cookie_str) {
		sendJson(res, 400, {
			success: false,
			message: "platform, sub_platform, cookie_str are required"
		});
		return;
	}
	const cookies = parseCookies(cookie_str);
	const idKey = accountIdKey(platform, sub_platform);
	const accountId = cookies[idKey] ?? "";
	if (!accountId) {
		sendJson(res, 400, {
			success: false,
			message: `Cookie 中缺少 ${idKey} 字段，无法识别账号`
		});
		return;
	}
	const dir = credDir(platform);
	await fs.mkdir(dir, { recursive: true });
	const nick = decodeCookieNick(cookies.tracknick || cookies.dnk || cookies._nk_ || "");
	const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
	const credData = {
		cookie_str,
		updatedAt,
		profile: {
			account_id: accountId,
			account_name: nick,
			main_cate_name: ""
		}
	};
	if (expires && Object.keys(expires).length > 0) credData.expires = expires;
	const filePath = path.join(dir, `${accountId}.json`);
	await fs.writeFile(filePath, JSON.stringify(credData, null, 2), "utf-8");
	await writeDefaultAccountId(platform, accountId);
	sendJson(res, 200, {
		success: true,
		account_id: accountId,
		message: "凭证已保存"
	});
	enrichProfile(platform, cookies, cookie_str, filePath, updatedAt).catch(() => {});
}
async function enrichProfile(platform, cookies, cookieStr, filePath, expectedUpdatedAt) {
	if (platform !== "taobao") return;
	try {
		const token = cookies._tb_token_ ?? "";
		const resp = await fetch(`https://sycm.taobao.com/custom/menu/getPersonalView.json?token=${token}`, {
			headers: {
				Cookie: cookieStr,
				Referer: "https://sycm.taobao.com/"
			},
			signal: AbortSignal.timeout(5e3)
		});
		if (!resp.ok) return;
		const json = await resp.json();
		if (json.code === 0 && json.data) {
			const text = await fs.readFile(filePath, "utf-8");
			const cred = JSON.parse(text);
			if (cred.updatedAt !== expectedUpdatedAt) return;
			const nick = decodeCookieNick(cookies.tracknick || cookies.dnk || cookies._nk_ || "");
			cred.profile = {
				account_id: cookies.unb ?? "",
				account_name: json.data.runAsShopTitle || json.data.loginUserName || json.data.mainUserName || nick,
				main_cate_name: json.data.mainCategoryName || ""
			};
			await fs.writeFile(filePath, JSON.stringify(cred, null, 2), "utf-8");
		}
	} catch {}
}
async function handleExpiry(req, res, url) {
	const platform = url.searchParams.get("platform") ?? "";
	if (!platform) {
		sendJson(res, 400, { error: "platform is required" });
		return;
	}
	const subPlatform = url.searchParams.get("sub_platform") ?? platform;
	const accountId = await readDefaultAccountId(platform);
	if (!accountId) {
		sendJson(res, 200, {
			platform,
			sub_platform: subPlatform,
			earliest_expiry: null
		});
		return;
	}
	const expires = (await readCredFile(platform, accountId))?.expires;
	if (!expires || Object.keys(expires).length === 0) {
		sendJson(res, 200, {
			platform,
			sub_platform: subPlatform,
			earliest_expiry: null
		});
		return;
	}
	const now = Date.now() / 1e3;
	const required = REQUIRED_COOKIES[platformKey(platform, subPlatform)];
	const requiredValues = required ? required.map((k) => expires[k]).filter((v) => typeof v === "number" && v > 0) : [];
	let earliest;
	if (requiredValues.length > 0) earliest = Math.min(...requiredValues);
	else if (required && required.length > 0) earliest = null;
	else {
		const allValues = Object.values(expires).filter((v) => v > 0);
		earliest = allValues.length > 0 ? Math.min(...allValues) : null;
	}
	sendJson(res, 200, {
		platform,
		sub_platform: subPlatform,
		earliest_expiry: earliest,
		expired: earliest !== null && earliest < now,
		expires
	});
}
const VERIFY_URLS = {
	"taobao:sycm": {
		url: "https://sycm.taobao.com/portal/home.htm",
		rejectPatterns: [
			"没开店或店铺状态不正常",
			"没有权限",
			"无权访问",
			"no_permission",
			"No%20Shop",
			"login.taobao.com"
		]
	},
	"taobao:wxt": {
		url: "https://alimama.taobao.com/",
		rejectPatterns: [
			"没开店或店铺状态不正常",
			"没有权限",
			"无权访问",
			"no_permission",
			"login.taobao.com"
		]
	}
};
async function handleVerifyAccess(_req, res, url) {
	const platform = url.searchParams.get("platform") ?? "";
	const subPlatform = url.searchParams.get("sub_platform") ?? "";
	if (!platform || !subPlatform) {
		sendJson(res, 400, { error: "platform and sub_platform are required" });
		return;
	}
	const cfg = VERIFY_URLS[`${platform}:${subPlatform}`];
	if (!cfg) {
		sendJson(res, 200, {
			accessible: true,
			reason: "no_verify_config"
		});
		return;
	}
	const accountId = await readDefaultAccountId(platform);
	if (!accountId) {
		sendJson(res, 200, {
			accessible: false,
			reason: "no_credentials"
		});
		return;
	}
	const data = await readCredFile(platform, accountId);
	const cookieStr = typeof data?.cookie_str === "string" ? data.cookie_str : "";
	if (!cookieStr) {
		sendJson(res, 200, {
			accessible: false,
			reason: "empty_cookie"
		});
		return;
	}
	try {
		const resp = await fetch(cfg.url, {
			headers: {
				Cookie: cookieStr,
				"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
			},
			redirect: "follow",
			signal: AbortSignal.timeout(1e4)
		});
		if (cfg.rejectPatterns.some((p) => resp.url.includes(p))) {
			sendJson(res, 200, {
				accessible: false,
				reason: "permission_denied"
			});
			return;
		}
		if (resp.status !== 200) {
			sendJson(res, 200, {
				accessible: false,
				reason: "http_error"
			});
			return;
		}
		const reader = resp.body?.getReader();
		let text = "";
		if (reader) {
			const decoder = new TextDecoder();
			let bytesRead = 0;
			const maxBytes = 32 * 1024;
			while (bytesRead < maxBytes) {
				const { done, value } = await reader.read();
				if (done) break;
				text += decoder.decode(value, { stream: true });
				bytesRead += value.byteLength;
			}
			reader.cancel().catch(() => {});
		}
		const rejected = cfg.rejectPatterns.some((p) => text.includes(p));
		sendJson(res, 200, {
			accessible: !rejected,
			reason: rejected ? "permission_denied" : "ok"
		});
	} catch (err) {
		sendJson(res, 200, {
			accessible: false,
			reason: `fetch_error: ${String(err)}`
		});
	}
}
async function handleDelete(req, res) {
	const body = await readJsonBodyOrError(req, res, 64 * 1024);
	if (body === void 0) return;
	const { platform, sub_platform, account_id } = body;
	if (!platform || !sub_platform || !account_id) {
		sendJson(res, 400, {
			success: false,
			message: "platform, sub_platform, account_id are required"
		});
		return;
	}
	const dir = credDir(platform);
	const credFile = path.join(dir, `${account_id}.json`);
	try {
		await fs.unlink(credFile);
	} catch {
		sendJson(res, 404, {
			success: false,
			message: `账号不存在: ${account_id}`
		});
		return;
	}
	if (await readDefaultAccountId(platform) === account_id) {
		let files = [];
		try {
			files = await fs.readdir(dir);
		} catch {}
		const remaining = files.filter((f) => f.endsWith(".json")).map((f) => f.slice(0, -5));
		if (remaining.length > 0) await writeDefaultAccountId(platform, remaining[0]);
		else await fs.unlink(path.join(dir, "default")).catch(() => {});
	}
	sendJson(res, 200, {
		success: true,
		message: "凭证已删除"
	});
}
async function handleCredentialsProxyHttpRequest(req, res, opts) {
	const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
	if (!isCredentialsProxyPath(url.pathname)) return false;
	if (req.method !== "GET" && req.method !== "POST") {
		sendMethodNotAllowed(res, "GET, POST");
		return true;
	}
	if (!await authorizeControlUiReadRequest(req, res, {
		auth: opts.auth,
		trustedProxies: opts.trustedProxies,
		allowRealIpFallback: opts.allowRealIpFallback,
		rateLimiter: opts.rateLimiter
	})) return true;
	if (url.pathname === "/credentials/status" && req.method === "GET") {
		await handleStatus(req, res, url);
		return true;
	}
	if (url.pathname === "/credentials/accounts" && req.method === "GET") {
		await handleAccounts(req, res, url);
		return true;
	}
	if (url.pathname === "/credentials/expiry" && req.method === "GET") {
		await handleExpiry(req, res, url);
		return true;
	}
	if (url.pathname === "/credentials/verify-access" && req.method === "GET") {
		await handleVerifyAccess(req, res, url);
		return true;
	}
	if (url.pathname === "/credentials/save" && req.method === "POST") {
		await handleSave(req, res);
		return true;
	}
	if (url.pathname === "/credentials/delete" && req.method === "POST") {
		await handleDelete(req, res);
		return true;
	}
	sendJson(res, 404, { error: "not_found" });
	return true;
}
//#endregion
export { writeCredentialsDefaultAccount as a, isCredentialsProxyPath as i, __clearCredentialsStatusCacheForTests as n, handleCredentialsProxyHttpRequest as r, CREDENTIALS_DIR as t };
