import CryptoJS from 'crypto-js';
import { APP_CONFIG } from '@/config';

interface StorageEnvelope<T> {
	value: T;
	expiresAt: number;
}

const AES_KEY = CryptoJS.enc.Hex.parse(CryptoJS.SHA256(APP_CONFIG.STORAGE_ENCRYPTION_KEY).toString());
const AES_IV = CryptoJS.enc.Hex.parse(CryptoJS.MD5(APP_CONFIG.STORAGE_ENCRYPTION_KEY).toString());

const encrypt = (value: unknown) => {
	const payload = CryptoJS.enc.Utf8.parse(JSON.stringify(value));
	return CryptoJS.AES.encrypt(payload, AES_KEY, {
		iv: AES_IV,
		mode: CryptoJS.mode.CBC,
		padding: CryptoJS.pad.Pkcs7
	}).toString();
};

const decrypt = <T>(value: string): T | null => {
	try {
		const decrypted = CryptoJS.AES.decrypt(value, AES_KEY, {
			iv: AES_IV,
			mode: CryptoJS.mode.CBC,
			padding: CryptoJS.pad.Pkcs7
		}).toString(CryptoJS.enc.Utf8);
		if (decrypted) {
			return JSON.parse(decrypted) as T;
		}

		// Backward compatibility for old passphrase-based ciphertexts.
		const legacy = CryptoJS.AES.decrypt(value, APP_CONFIG.STORAGE_ENCRYPTION_KEY).toString(CryptoJS.enc.Utf8);
		return legacy ? JSON.parse(legacy) as T : null;
	} catch (error) {
		return null;
	}
};

export const encryptedStorage = {
	set<T>(key: string, value: T, expiresInSeconds = 0) {
		const envelope: StorageEnvelope<T> = {
			value,
			expiresAt: expiresInSeconds > 0 ? Date.now() + expiresInSeconds * 1000 : 0
		};
		uni.setStorageSync(key, encrypt(envelope));
	},

	get<T>(key: string): T | null {
		const encryptedValue = uni.getStorageSync(key);
		if (typeof encryptedValue !== 'string' || !encryptedValue) return null;

		const envelope = decrypt<StorageEnvelope<T>>(encryptedValue);
		if (!envelope || typeof envelope !== 'object' || !('value' in envelope)) {
			uni.removeStorageSync(key);
			return null;
		}

		if (envelope.expiresAt > 0 && envelope.expiresAt <= Date.now()) {
			uni.removeStorageSync(key);
			return null;
		}

		return envelope.value;
	},

	remove(key: string) {
		uni.removeStorageSync(key);
	}
};

let cachedTokenLogged = false;

export const authStorage = {
	getToken() {
		const token = encryptedStorage.get<string>(APP_CONFIG.STORAGE_TOKEN_KEY) || '';
		if (token && !cachedTokenLogged) {
			cachedTokenLogged = true;
			console.log('[cashier] token:', token);
		}
		return token;
	},

	setToken(token: string, expiresInSeconds = 0) {
		encryptedStorage.set(APP_CONFIG.STORAGE_TOKEN_KEY, token, expiresInSeconds);
		cachedTokenLogged = true;
		console.log('[cashier] token:', token);
	},

	clearToken() {
		encryptedStorage.remove(APP_CONFIG.STORAGE_TOKEN_KEY);
		cachedTokenLogged = false;
	}
};
