import * as fs from 'fs';
import * as path from 'path';
import axios from 'axios';
import { getMenuSladovnicka, getMenuTechTower, getMenuSenkSerikova, StaleWeekError } from '../restaurants';

jest.mock('axios');
const mockedAxios = axios as jest.Mocked<typeof axios>;

const fixturesDir = path.join(__dirname, 'fixtures');
const loadFixture = (name: string) => fs.readFileSync(path.join(fixturesDir, name), 'utf-8');

// Pondělí 12.5.2025
const MONDAY = new Date('2025-05-12');

beforeEach(() => {
    jest.resetAllMocks();
});

describe('Sladovnicka parser', () => {
    beforeEach(() => {
        mockedAxios.get = jest.fn().mockResolvedValue({ data: loadFixture('sladovnicka.html') });
    });

    test('vrátí pole o délce 5 (jeden záznam na každý pracovní den)', async () => {
        const menu = await getMenuSladovnicka(MONDAY);
        expect(menu).toHaveLength(5);
    });

    test('pondělní menu obsahuje aspoň jedno jídlo', async () => {
        const menu = await getMenuSladovnicka(MONDAY);
        expect(menu[0].length).toBeGreaterThan(0);
    });

    test('první položka pondělního dne je polévka (isSoup=true)', async () => {
        const menu = await getMenuSladovnicka(MONDAY);
        expect(menu[0][0].isSoup).toBe(true);
    });

    test('jídla mají name, price a amount', async () => {
        const menu = await getMenuSladovnicka(MONDAY);
        const jidlo = menu[0][1];
        expect(jidlo.name).toBeTruthy();
        expect(jidlo.price).toBeTruthy();
        expect(jidlo.amount).toBeTruthy();
    });

    test('alergeny jsou naparsovány jako čísla', async () => {
        const menu = await getMenuSladovnicka(MONDAY);
        const polievka = menu[0][0];
        expect(Array.isArray(polievka.allergens)).toBe(true);
        expect(polievka.allergens!.length).toBeGreaterThan(0);
        expect(typeof polievka.allergens![0]).toBe('number');
    });
});

describe('TechTower parser', () => {
    beforeEach(() => {
        mockedAxios.get = jest.fn().mockResolvedValue({ data: loadFixture('techtower.html') });
    });

    test('vrátí pole o délce 5', async () => {
        const menu = await getMenuTechTower(MONDAY);
        expect(menu).toHaveLength(5);
    });

    test('pondělní menu obsahuje polévku a hlavní jídla', async () => {
        const menu = await getMenuTechTower(MONDAY);
        expect(menu[0].some(f => f.isSoup)).toBe(true);
        expect(menu[0].some(f => !f.isSoup)).toBe(true);
    });

    test('TechTower hodí StaleWeekError, pokud datum v hlavičce neodpovídá', async () => {
        // Fixture obsahuje "12.5.-16.5.2025" – jiný týden = stale
        const jinaStreda = new Date('2025-04-14');
        await expect(getMenuTechTower(jinaStreda)).rejects.toBeInstanceOf(StaleWeekError);
    });

    test('StaleWeekError obsahuje naparsovaná data', async () => {
        const jinaStreda = new Date('2025-04-14');
        try {
            await getMenuTechTower(jinaStreda);
        } catch (e) {
            expect(e).toBeInstanceOf(StaleWeekError);
            const err = e as StaleWeekError;
            expect(err.food).toHaveLength(5);
        }
    });
});

describe('SenkSerikova parser', () => {
    beforeEach(() => {
        // SenkSerikova parsuje arraybuffer – musíme vrátit Buffer, ne string
        mockedAxios.get = jest.fn().mockResolvedValue({
            data: Buffer.from(loadFixture('senkserikova.html')),
            headers: {}
        });
    });

    test('parser provede HTTP request a vrátí pole', async () => {
        const menu = await getMenuSenkSerikova(MONDAY);
        expect(mockedAxios.get).toHaveBeenCalledTimes(1);
        expect(Array.isArray(menu)).toBe(true);
    });

    test('výsledné dny s obsahem mají správnou strukturu (name, price, isSoup)', async () => {
        const menu = await getMenuSenkSerikova(MONDAY);
        // Protože MONDAY je v minulosti, parser vrátí placeholdery pro všechny pracovní
        // dny a .menicka elementy přidá za ně – hledáme aspoň jeden den s reálnými daty
        const denSJidlem = menu.find(den =>
            den.length > 0 && den[0].name !== 'Pro tento den není uveřejněna nabídka jídel'
        );
        if (denSJidlem) {
            expect(typeof denSJidlem[0].name).toBe('string');
            expect(typeof denSJidlem[0].isSoup).toBe('boolean');
        }
    });
});
