import { existsSync, mkdirSync } from 'fs';
import { dirname, resolve } from 'path';
import { DatabaseSync } from 'node:sqlite';

function getCacheDbPath() {
	return process.env.EMBY_USER_CACHE_DB_PATH || resolve('.cache', 'emby-users.db');
}

function ensureCacheDir() {
	const dir = dirname(getCacheDbPath());
	if (!existsSync(dir)) {
		mkdirSync(dir, { recursive: true });
	}
}

function openCacheDb() {
	ensureCacheDir();
	const db = new DatabaseSync(getCacheDbPath());
	db.exec(`
		CREATE TABLE IF NOT EXISTS EmbyUsers (
			embyGuid TEXT PRIMARY KEY,
			name TEXT NOT NULL,
			fetchedAt TEXT NOT NULL
		);
		CREATE TABLE IF NOT EXISTS CacheMeta (
			key TEXT PRIMARY KEY,
			value TEXT NOT NULL
		);
	`);
	return db;
}

function normalizeGuid(value) {
	return typeof value === 'string' ? value.replace(/-/g, '').trim().toLowerCase() : '';
}

export function normalizeEmbyUsers(users) {
	if (!Array.isArray(users)) return [];

	return users
		.map((user) => ({
			embyGuid: normalizeGuid(user?.embyGuid ?? user?.Id),
			name: String(user?.name ?? user?.Name ?? '').trim()
		}))
		.filter((user) => user.embyGuid && user.name);
}

export function writeCachedEmbyUsers(users) {
	const normalizedUsers = normalizeEmbyUsers(users);
	const fetchedAt = new Date().toISOString();
	const db = openCacheDb();

	try {
		const clearStmt = db.prepare('DELETE FROM EmbyUsers');
		const insertStmt = db.prepare(
			'INSERT INTO EmbyUsers (embyGuid, name, fetchedAt) VALUES (?, ?, ?)'
		);
		const metaStmt = db.prepare(
			'INSERT INTO CacheMeta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value'
		);

		db.exec('BEGIN');
		try {
			clearStmt.run();
			for (const user of normalizedUsers) {
				insertStmt.run(user.embyGuid, user.name, fetchedAt);
			}
			metaStmt.run('lastSyncedAt', fetchedAt);
			db.exec('COMMIT');
		} catch (error) {
			db.exec('ROLLBACK');
			throw error;
		}
	} finally {
		db.close();
	}

	return { users: normalizedUsers, lastSyncedAt: fetchedAt };
}

export function readCachedEmbyUsers() {
	if (!existsSync(getCacheDbPath())) {
		return { users: [], lastSyncedAt: null };
	}

	const db = openCacheDb();
	try {
		const users = db
			.prepare('SELECT embyGuid, name, fetchedAt FROM EmbyUsers ORDER BY lower(name), embyGuid')
			.all()
			.map((row) => ({
				embyGuid: normalizeGuid(row.embyGuid),
				name: row.name
			}));
		const meta = db.prepare("SELECT value FROM CacheMeta WHERE key = 'lastSyncedAt'").get();

		return {
			users,
			lastSyncedAt: meta?.value || users[0]?.fetchedAt || null
		};
	} finally {
		db.close();
	}
}

export function getCachedEmbyUserMap() {
	return new Map(readCachedEmbyUsers().users.map((user) => [user.embyGuid, user]));
}

export function applyCachedEmbyNames(users) {
	const cached = readCachedEmbyUsers();
	const lookup = new Map(cached.users.map((user) => [user.embyGuid, user]));
	let matchedCount = 0;

	const hydratedUsers = (users || []).map((user) => {
		const embyGuid = normalizeGuid(user?.embyGuid);
		const cachedUser = lookup.get(embyGuid);
		if (!cachedUser) {
			return user;
		}

		matchedCount++;
		return {
			...user,
			dbName: user.dbName || user.name,
			embyName: cachedUser.name,
			name: cachedUser.name
		};
	});

	return {
		users: hydratedUsers,
		cache: {
			matchedCount,
			totalCachedUsers: lookup.size,
			lastSyncedAt: cached.lastSyncedAt
		}
	};
}
