import * as os from 'os';
import * as path from 'path';
import * as fs from 'fs';
import { StorageInterface } from '../storage/StorageInterface';
import { resetMemoryStorage } from '../storage/memory';
import MemoryStorage from '../storage/memory';
import JsonStorage from '../storage/json';

const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'luncher-test-'));
const tempDbPath = path.join(tempDir, 'test-db.json');

// Parametrické spuštění stejné sady testů pro obě implementace
const implementations: [string, () => StorageInterface, () => void][] = [
    ['MemoryStorage', () => new MemoryStorage(), resetMemoryStorage],
    ['JsonStorage', () => {
        // Zajistíme čistý stav souboru před každým testem
        if (fs.existsSync(tempDbPath)) {
            fs.unlinkSync(tempDbPath);
        }
        // JsonStorage načte/vytvoří soubor při inicializaci, musíme obalit
        const JsonStorageDynamic = require('../storage/json').default;
        // Přepíšeme dbPath přes prototyp – pro testy použijeme tmpdir
        const inst = Object.create(JsonStorageDynamic.prototype);
        const JSONdb = require('simple-json-db');
        (inst as any).db = new JSONdb(tempDbPath);
        inst.hasData = async (key: string) => Promise.resolve((inst as any).db.has(key));
        inst.getData = async (key: string) => (inst as any).db.get(key);
        inst.setData = async (key: string, data: any) => { (inst as any).db.set(key, data); return Promise.resolve(); };
        return inst;
    }, () => {
        if (fs.existsSync(tempDbPath)) {
            fs.unlinkSync(tempDbPath);
        }
    }],
];

describe.each(implementations)('%s splňuje StorageInterface kontrakt', (name, factory, reset) => {
    let storage: StorageInterface;

    beforeEach(() => {
        reset();
        storage = factory();
    });

    test('hasData vrátí false pro neexistující klíč', async () => {
        expect(await storage.hasData('neexistujici')).toBe(false);
    });

    test('setData + hasData vrátí true', async () => {
        await storage.setData('klic', { value: 1 });
        expect(await storage.hasData('klic')).toBe(true);
    });

    test('setData + getData vrátí uložená data', async () => {
        const data = { name: 'Jan', score: 42 };
        await storage.setData('testkey', data);
        const result = await storage.getData('testkey');
        expect(result).toEqual(data);
    });

    test('getData pro neexistující klíč vrátí undefined', async () => {
        const result = await storage.getData('neexistujici');
        expect(result).toBeUndefined();
    });

    test('setData přepíše existující data', async () => {
        await storage.setData('klic', { version: 1 });
        await storage.setData('klic', { version: 2 });
        const result = await storage.getData<{ version: number }>('klic');
        expect(result?.version).toBe(2);
    });

    test('různé klíče jsou nezávislé', async () => {
        await storage.setData('a', { val: 'A' });
        await storage.setData('b', { val: 'B' });
        expect((await storage.getData<{ val: string }>('a'))?.val).toBe('A');
        expect((await storage.getData<{ val: string }>('b'))?.val).toBe('B');
    });
});

afterAll(() => {
    if (fs.existsSync(tempDir)) {
        fs.rmSync(tempDir, { recursive: true });
    }
});
