import { n as normalizeAccountId } from "./account-id-BcgJyVVC.js";
import { a as resolveMatrixDefaultOrOnlyAccountId, n as requiresExplicitMatrixDefaultAccount } from "./account-selection-Ck-30rC1.js";
import { t as getMatrixRuntime } from "./runtime-BQhSkkJu.js";
import { n as resolveMatrixAccountStorageRoot, s as resolveMatrixLegacyFlatStoragePaths } from "./storage-paths-Dkvvbk-5.js";
import fs from "node:fs";
import path from "node:path";
import os from "node:os";
//#region extensions/matrix/src/matrix/client/storage.ts
const DEFAULT_ACCOUNT_KEY = "default";
const STORAGE_META_FILENAME = "storage-meta.json";
const THREAD_BINDINGS_FILENAME = "thread-bindings.json";
const LEGACY_CRYPTO_MIGRATION_FILENAME = "legacy-crypto-migration.json";
const RECOVERY_KEY_FILENAME = "recovery-key.json";
const IDB_SNAPSHOT_FILENAME = "crypto-idb-snapshot.json";
const STARTUP_VERIFICATION_FILENAME = "startup-verification.json";
function resolveLegacyStoragePaths(env = process.env) {
	const legacy = resolveMatrixLegacyFlatStoragePaths(getMatrixRuntime().state.resolveStateDir(env, os.homedir));
	return {
		storagePath: legacy.storagePath,
		cryptoPath: legacy.cryptoPath
	};
}
function assertLegacyMigrationAccountSelection(params) {
	const cfg = getMatrixRuntime().config.current();
	if (!cfg.channels?.matrix || typeof cfg.channels.matrix !== "object") return;
	if (requiresExplicitMatrixDefaultAccount(cfg)) throw new Error("Legacy Matrix client storage cannot be migrated automatically because multiple Matrix accounts are configured and channels.matrix.defaultAccount is not set.");
	const selectedAccountId = normalizeAccountId(resolveMatrixDefaultOrOnlyAccountId(cfg));
	const currentAccountId = normalizeAccountId(params.accountKey);
	if (selectedAccountId !== currentAccountId) throw new Error(`Legacy Matrix client storage targets account "${selectedAccountId}", but the current client is starting account "${currentAccountId}". Start the selected account first so flat legacy storage is not migrated into the wrong account directory.`);
}
function scoreStorageRoot(rootDir) {
	let score = 0;
	if (fs.existsSync(path.join(rootDir, "bot-storage.json"))) score += 8;
	if (fs.existsSync(path.join(rootDir, "crypto"))) score += 8;
	if (fs.existsSync(path.join(rootDir, THREAD_BINDINGS_FILENAME))) score += 4;
	if (fs.existsSync(path.join(rootDir, LEGACY_CRYPTO_MIGRATION_FILENAME))) score += 3;
	if (fs.existsSync(path.join(rootDir, RECOVERY_KEY_FILENAME))) score += 2;
	if (fs.existsSync(path.join(rootDir, IDB_SNAPSHOT_FILENAME))) score += 2;
	if (fs.existsSync(path.join(rootDir, STORAGE_META_FILENAME))) score += 1;
	return score;
}
function resolveStorageRootMtimeMs(rootDir) {
	try {
		return fs.statSync(rootDir).mtimeMs;
	} catch {
		return 0;
	}
}
function readStoredRootMetadata(rootDir) {
	const metadata = {};
	try {
		const parsed = JSON.parse(fs.readFileSync(path.join(rootDir, STORAGE_META_FILENAME), "utf8"));
		if (typeof parsed.homeserver === "string" && parsed.homeserver.trim()) metadata.homeserver = parsed.homeserver.trim();
		if (typeof parsed.userId === "string" && parsed.userId.trim()) metadata.userId = parsed.userId.trim();
		if (typeof parsed.accountId === "string" && parsed.accountId.trim()) metadata.accountId = parsed.accountId.trim();
		if (typeof parsed.accessTokenHash === "string" && parsed.accessTokenHash.trim()) metadata.accessTokenHash = parsed.accessTokenHash.trim();
		if (typeof parsed.deviceId === "string" && parsed.deviceId.trim()) metadata.deviceId = parsed.deviceId.trim();
		if (parsed.currentTokenStateClaimed === true) metadata.currentTokenStateClaimed = true;
		if (typeof parsed.createdAt === "string" && parsed.createdAt.trim()) metadata.createdAt = parsed.createdAt.trim();
	} catch {}
	try {
		const parsed = JSON.parse(fs.readFileSync(path.join(rootDir, STARTUP_VERIFICATION_FILENAME), "utf8"));
		if (!metadata.deviceId && typeof parsed.deviceId === "string" && parsed.deviceId.trim()) metadata.deviceId = parsed.deviceId.trim();
	} catch {}
	return metadata;
}
function isCompatibleStorageRoot(params) {
	const metadata = readStoredRootMetadata(params.candidateRootDir);
	if (metadata.homeserver && metadata.homeserver !== params.homeserver) return false;
	if (metadata.userId && metadata.userId !== params.userId) return false;
	if (metadata.accountId && normalizeAccountId(metadata.accountId) !== normalizeAccountId(params.accountKey)) return false;
	if (params.deviceId && metadata.deviceId && metadata.deviceId.trim() && metadata.deviceId.trim() !== params.deviceId.trim()) return false;
	if (params.requireExplicitDeviceMatch && params.deviceId && (!metadata.deviceId || metadata.deviceId.trim() !== params.deviceId.trim())) return false;
	return true;
}
function resolvePreferredMatrixStorageRoot(params) {
	const parentDir = path.dirname(params.canonicalRootDir);
	const bestCurrentScore = scoreStorageRoot(params.canonicalRootDir);
	let best = {
		rootDir: params.canonicalRootDir,
		tokenHash: params.canonicalTokenHash,
		score: bestCurrentScore,
		mtimeMs: resolveStorageRootMtimeMs(params.canonicalRootDir)
	};
	if (!params.deviceId?.trim()) return {
		rootDir: best.rootDir,
		tokenHash: best.tokenHash
	};
	const canonicalMetadata = readStoredRootMetadata(params.canonicalRootDir);
	if (canonicalMetadata.accessTokenHash === params.canonicalTokenHash && canonicalMetadata.deviceId?.trim() === params.deviceId.trim() && canonicalMetadata.currentTokenStateClaimed === true) return {
		rootDir: best.rootDir,
		tokenHash: best.tokenHash
	};
	let siblingEntries = [];
	try {
		siblingEntries = fs.readdirSync(parentDir, { withFileTypes: true });
	} catch {
		return {
			rootDir: best.rootDir,
			tokenHash: best.tokenHash
		};
	}
	for (const entry of siblingEntries) {
		if (!entry.isDirectory()) continue;
		if (entry.name === params.canonicalTokenHash) continue;
		const candidateRootDir = path.join(parentDir, entry.name);
		if (!isCompatibleStorageRoot({
			candidateRootDir,
			homeserver: params.homeserver,
			userId: params.userId,
			accountKey: params.accountKey,
			deviceId: params.deviceId,
			requireExplicitDeviceMatch: Boolean(params.deviceId)
		})) continue;
		const candidateScore = scoreStorageRoot(candidateRootDir);
		if (candidateScore <= 0) continue;
		const candidateMtimeMs = resolveStorageRootMtimeMs(candidateRootDir);
		if (candidateScore > best.score || best.rootDir !== params.canonicalRootDir && candidateScore === best.score && candidateMtimeMs > best.mtimeMs) best = {
			rootDir: candidateRootDir,
			tokenHash: entry.name,
			score: candidateScore,
			mtimeMs: candidateMtimeMs
		};
	}
	return {
		rootDir: best.rootDir,
		tokenHash: best.tokenHash
	};
}
function resolveMatrixStoragePaths(params) {
	const env = params.env ?? process.env;
	const canonical = resolveMatrixAccountStorageRoot({
		stateDir: params.stateDir ?? getMatrixRuntime().state.resolveStateDir(env, os.homedir),
		homeserver: params.homeserver,
		userId: params.userId,
		accessToken: params.accessToken,
		accountId: params.accountId
	});
	const { rootDir, tokenHash } = resolvePreferredMatrixStorageRoot({
		canonicalRootDir: canonical.rootDir,
		canonicalTokenHash: canonical.tokenHash,
		homeserver: params.homeserver,
		userId: params.userId,
		accountKey: canonical.accountKey,
		deviceId: params.deviceId
	});
	return {
		rootDir,
		storagePath: path.join(rootDir, "bot-storage.json"),
		cryptoPath: path.join(rootDir, "crypto"),
		metaPath: path.join(rootDir, STORAGE_META_FILENAME),
		recoveryKeyPath: path.join(rootDir, "recovery-key.json"),
		idbSnapshotPath: path.join(rootDir, IDB_SNAPSHOT_FILENAME),
		accountKey: canonical.accountKey,
		tokenHash
	};
}
function resolveMatrixStateFilePath(params) {
	const storagePaths = resolveMatrixStoragePaths({
		homeserver: params.auth.homeserver,
		userId: params.auth.userId,
		accessToken: params.auth.accessToken,
		accountId: params.accountId ?? params.auth.accountId,
		deviceId: params.auth.deviceId,
		env: params.env,
		stateDir: params.stateDir
	});
	return path.join(storagePaths.rootDir, params.filename);
}
async function maybeMigrateLegacyStorage(params) {
	const legacy = resolveLegacyStoragePaths(params.env);
	const hasLegacyStorage = fs.existsSync(legacy.storagePath);
	const hasLegacyCrypto = fs.existsSync(legacy.cryptoPath);
	if (!hasLegacyStorage && !hasLegacyCrypto) return;
	const hasTargetStorage = fs.existsSync(params.storagePaths.storagePath);
	const hasTargetCrypto = fs.existsSync(params.storagePaths.cryptoPath);
	const shouldMigrateStorage = hasLegacyStorage && !hasTargetStorage;
	const shouldMigrateCrypto = hasLegacyCrypto && !hasTargetCrypto;
	if (!shouldMigrateStorage && !shouldMigrateCrypto) return;
	assertLegacyMigrationAccountSelection({ accountKey: params.storagePaths.accountKey });
	const logger = getMatrixRuntime().logging.getChildLogger({ module: "matrix-storage" });
	const { maybeCreateMatrixMigrationSnapshot } = await import("./migration-snapshot.runtime.js");
	await maybeCreateMatrixMigrationSnapshot({
		trigger: "matrix-client-fallback",
		env: params.env,
		log: logger
	});
	fs.mkdirSync(params.storagePaths.rootDir, { recursive: true });
	const moved = [];
	const skippedExistingTargets = [];
	try {
		if (shouldMigrateStorage) moveLegacyStoragePathOrThrow({
			sourcePath: legacy.storagePath,
			targetPath: params.storagePaths.storagePath,
			label: "sync store",
			moved
		});
		else if (hasLegacyStorage) skippedExistingTargets.push(`- sync store remains at ${legacy.storagePath} because ${params.storagePaths.storagePath} already exists`);
		if (shouldMigrateCrypto) moveLegacyStoragePathOrThrow({
			sourcePath: legacy.cryptoPath,
			targetPath: params.storagePaths.cryptoPath,
			label: "crypto store",
			moved
		});
		else if (hasLegacyCrypto) skippedExistingTargets.push(`- crypto store remains at ${legacy.cryptoPath} because ${params.storagePaths.cryptoPath} already exists`);
	} catch (err) {
		const rollbackError = rollbackLegacyMoves(moved);
		throw new Error(rollbackError ? `Failed migrating legacy Matrix client storage: ${String(err)}. Rollback also failed: ${rollbackError}` : `Failed migrating legacy Matrix client storage: ${String(err)}`, { cause: err });
	}
	if (moved.length > 0) logger.info(`matrix: migrated legacy client storage into ${params.storagePaths.rootDir}\n${moved.map((entry) => `- ${entry.label}: ${entry.sourcePath} -> ${entry.targetPath}`).join("\n")}`);
	if (skippedExistingTargets.length > 0) logger.warn?.(`matrix: legacy client storage still exists in the flat path because some account-scoped targets already existed.\n${skippedExistingTargets.join("\n")}`);
}
function moveLegacyStoragePathOrThrow(params) {
	if (!fs.existsSync(params.sourcePath)) return;
	if (fs.existsSync(params.targetPath)) throw new Error(`legacy Matrix ${params.label} target already exists (${params.targetPath}); refusing to overwrite it automatically`);
	fs.renameSync(params.sourcePath, params.targetPath);
	params.moved.push({
		sourcePath: params.sourcePath,
		targetPath: params.targetPath,
		label: params.label
	});
}
function rollbackLegacyMoves(moved) {
	for (const entry of moved.toReversed()) try {
		if (!fs.existsSync(entry.targetPath) || fs.existsSync(entry.sourcePath)) continue;
		fs.renameSync(entry.targetPath, entry.sourcePath);
	} catch (err) {
		return `${entry.label} (${entry.targetPath} -> ${entry.sourcePath}): ${String(err)}`;
	}
	return null;
}
function writeStoredRootMetadata(metaPath, payload) {
	try {
		fs.mkdirSync(path.dirname(metaPath), { recursive: true });
		fs.writeFileSync(metaPath, JSON.stringify(payload, null, 2), "utf-8");
		return true;
	} catch {
		return false;
	}
}
function writeStorageMeta(params) {
	const existing = readStoredRootMetadata(params.storagePaths.rootDir);
	return writeStoredRootMetadata(params.storagePaths.metaPath, {
		homeserver: params.homeserver,
		userId: params.userId,
		accountId: params.accountId ?? DEFAULT_ACCOUNT_KEY,
		accessTokenHash: params.storagePaths.tokenHash,
		deviceId: params.deviceId ?? null,
		currentTokenStateClaimed: params.currentTokenStateClaimed ?? existing.currentTokenStateClaimed === true,
		createdAt: existing.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
	});
}
function claimCurrentTokenStorageState(params) {
	const metadata = readStoredRootMetadata(params.rootDir);
	if (!metadata.accessTokenHash?.trim()) return false;
	return writeStoredRootMetadata(path.join(params.rootDir, STORAGE_META_FILENAME), {
		homeserver: metadata.homeserver,
		userId: metadata.userId,
		accountId: metadata.accountId ?? DEFAULT_ACCOUNT_KEY,
		accessTokenHash: metadata.accessTokenHash,
		deviceId: metadata.deviceId ?? null,
		currentTokenStateClaimed: true,
		createdAt: metadata.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
	});
}
function repairCurrentTokenStorageMetaDeviceId(params) {
	return writeStorageMeta({
		storagePaths: resolveMatrixStoragePaths({
			homeserver: params.homeserver,
			userId: params.userId,
			accessToken: params.accessToken,
			accountId: params.accountId,
			env: params.env,
			stateDir: params.stateDir
		}),
		homeserver: params.homeserver,
		userId: params.userId,
		accountId: params.accountId,
		deviceId: params.deviceId
	});
}
//#endregion
export { resolveMatrixStoragePaths as a, resolveMatrixStateFilePath as i, maybeMigrateLegacyStorage as n, writeStorageMeta as o, repairCurrentTokenStorageMetaDeviceId as r, claimCurrentTokenStorageState as t };
