import { safeStorage, app } from 'electron';
import * as fs from 'fs';
import * as path from 'path';

/**
 * SecureStorage - Replacement for electron-store using Electron's safeStorage API
 *
 * Uses OS-level encryption:
 * - macOS: Keychain
 * - Windows: DPAPI (Data Protection API)
 * - Linux: gnome-libsecret/kwallet
 *
 * Security notes:
 * - Protects against physical theft (when PC is off)
 * - Protects against other users on same machine
 * - Does NOT protect against malware running under same user
 * - On Linux, check getStorageBackend() - if 'basic_text', encryption is weak
 */

type StorageValue = string;
type StoredData = Record<string, string>;

class SecureStorage {
    private readonly storePath: string;
    private readonly cache: Map<string, StorageValue> = new Map();
    private isLoaded: boolean = false;

    constructor() {
        const userDataPath: string = app.getPath('userData');
        this.storePath = path.join(userDataPath, 'secure-config.json');
    }

    /**
     * Ensure data is loaded from disk (lazy loading)
     */
    private ensureLoaded(): void {
        if (!this.isLoaded) {
            this.loadFromDisk();
            this.isLoaded = true;
        }
    }

    /**
     * Load encrypted data from disk into memory cache
     */
    private loadFromDisk(): void {
        try {
            if (!fs.existsSync(this.storePath)) {
                return;
            }

            const fileData: string = fs.readFileSync(this.storePath, 'utf-8');
            const parsed: unknown = JSON.parse(fileData);

            if (typeof parsed !== 'object' || parsed === null) {
                console.error('[SecureStorage] Invalid data format in storage file');
                return;
            }

            for (const [key, unknownValue] of Object.entries(parsed)) {
                if (typeof unknownValue !== 'string' || unknownValue.length === 0) {
                    continue;
                }

                const storedValue: string = unknownValue;

                try {
                    if (storedValue.startsWith('encrypted:')) {
                        const encryptedBase64: string = storedValue.substring('encrypted:'.length);
                        const buffer: Buffer = Buffer.from(encryptedBase64, 'base64');
                        const decrypted: string = safeStorage.decryptString(buffer);
                        this.cache.set(key, decrypted);
                    } else if (storedValue.startsWith('plain:')) {
                        const plainValue: string = storedValue.substring('plain:'.length);
                        this.cache.set(key, plainValue);
                    } else {
                        try {
                            const buffer: Buffer = Buffer.from(storedValue, 'base64');
                            const decrypted: string = safeStorage.decryptString(buffer);
                            this.cache.set(key, decrypted);
                        } catch (decryptError: unknown) {
                            this.cache.set(key, storedValue);
                        }
                    }
                } catch (error: unknown) {
                    const errorMessage: string = error instanceof Error ? error.message : 'Unknown error';
                    console.error(`[SecureStorage] Failed to load key '${key}': ${errorMessage}`);
                }
            }
        } catch (error: unknown) {
            const errorMessage: string = error instanceof Error ? error.message : 'Unknown error';
            console.error(`[SecureStorage] Failed to load from disk: ${errorMessage}`);
        }
    }

    /**
     * Save encrypted data from memory cache to disk
     */
    private saveToDisk(): void {
        if (!safeStorage.isEncryptionAvailable()) {
            throw new Error('Encryption not available - cannot save securely');
        }

        const data: StoredData = {};

        for (const [key, value] of this.cache.entries()) {
            if (!value) {
                throw new Error(`Invalid value for key '${key}'`);
            }

            const buffer: Buffer = safeStorage.encryptString(value);
            if (!buffer || buffer.length === 0) {
                throw new Error(`Failed to encrypt key '${key}'`);
            }
            data[key] = `encrypted:${buffer.toString('base64')}`;
        }

        try {
            const dir: string = path.dirname(this.storePath);
            if (!fs.existsSync(dir)) {
                fs.mkdirSync(dir, { recursive: true });
            }

            fs.writeFileSync(this.storePath, JSON.stringify(data, null, 2), 'utf-8');
        } catch (error: unknown) {
            const errorMessage: string = error instanceof Error ? error.message : 'Unknown error';
            console.error(`[SecureStorage] Failed to save to disk: ${errorMessage}`);
            throw error;
        }
    }

    /**
     * Get a value from secure storage
     * @param key - Storage key
     * @param defaultValue - Default value if key doesn't exist
     * @returns Stored value or default
     */
    public get<T = string>(key: string, defaultValue: T | null = null): T | null {
        this.ensureLoaded();
        const value: StorageValue | undefined = this.cache.get(key);

        if (value === undefined) {
            return defaultValue;
        }

        try {
            return JSON.parse(value) as T;
        } catch {
            return value as unknown as T;
        }
    }

    /**
     * Set a value in secure storage (kept in memory only)
     * @param key - Storage key
     * @param value - Value to store
     */
    public set(key: string, value: unknown): void {
        this.ensureLoaded();
        const stringValue: string = typeof value === 'string' ? value : JSON.stringify(value);
        this.cache.set(key, stringValue);
    }

    /**
     * Delete a value from secure storage (memory only)
     * @param key - Storage key
     */
    public delete(key: string): void {
        this.ensureLoaded();
        this.cache.delete(key);
    }

    /**
     * Check if a key exists in secure storage
     * @param key - Storage key
     * @returns True if key exists
     */
    public has(key: string): boolean {
        this.ensureLoaded();
        return this.cache.has(key);
    }

    /**
     * Clear all data from secure storage (memory only)
     */
    public clear(): void {
        this.cache.clear();
    }

    /**
     * Manually save to disk (encrypted with safeStorage)
     * Call this when you want to persist data
     */
    public save(): void {
        this.saveToDisk();
    }

    /**
     * Check if encryption is available
     * @returns True if OS-level encryption is available
     */
    public isEncryptionAvailable(): boolean {
        return safeStorage.isEncryptionAvailable();
    }
}

declare global {
    var __secureStorageInstance: SecureStorage | undefined;
}

/**
 * Get the SecureStorage singleton instance
 * @returns SecureStorage instance
 */
export function getSecureStorage(): SecureStorage {
    if (!global.__secureStorageInstance) {
        global.__secureStorageInstance = new SecureStorage();

        if (!global.__secureStorageInstance.isEncryptionAvailable()) {
            console.warn(
                '[SecureStorage] WARNING: OS-level encryption is not available. ' +
                'Data will still be stored but with reduced security.'
            );
        }
    }
    return global.__secureStorageInstance;
}

export default SecureStorage;
