import webpush from 'web-push';
import crypto from 'crypto';
import getStorage from './storage';
import { getClientData, getToday } from './service';
import { getIsWeekend } from './utils';
import { LunchChoices } from '../../types';

const storage = getStorage();
const REGISTRY_KEY = 'push_reminder_registry';

interface RegistryEntry {
    time: string;
    subscription: webpush.PushSubscription;
}

type Registry = Record<string, RegistryEntry>;

/** Mapa login → timestamp (ms) posledního odeslání připomínky. */
const lastReminded = new Map<string, number>();

const REMINDER_COOLDOWN_MS = 60 * 60 * 1000; // 60 minut mezi připomínkami

function getCurrentTimeHHMM(): string {
    const now = new Date();
    return `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`;
}

/** Zjistí, zda má uživatel zvolenou nějakou možnost stravování. */
function userHasChoice(choices: LunchChoices, login: string): boolean {
    for (const locationKey of Object.keys(choices)) {
        const locationChoices = choices[locationKey as keyof LunchChoices];
        if (locationChoices && login in locationChoices) {
            return true;
        }
    }
    return false;
}

async function getRegistry(): Promise<Registry> {
    return await storage.getData<Registry>(REGISTRY_KEY) ?? {};
}

async function saveRegistry(registry: Registry): Promise<void> {
    await storage.setData(REGISTRY_KEY, registry);
}

/** Přidá nebo aktualizuje push subscription pro uživatele. */
export async function subscribePush(login: string, subscription: webpush.PushSubscription, reminderTime: string): Promise<void> {
    const registry = await getRegistry();
    registry[login] = { time: reminderTime, subscription };
    await saveRegistry(registry);
    console.log(`Push reminder: uživatel ${login} přihlášen k připomínkám v ${reminderTime}`);
}

/** Odebere push subscription pro uživatele. */
export async function unsubscribePush(login: string): Promise<void> {
    const registry = await getRegistry();
    delete registry[login];
    await saveRegistry(registry);
    lastReminded.delete(login);
    console.log(`Push reminder: uživatel ${login} odhlášen z připomínek`);
}

/** Vrátí veřejný VAPID klíč. */
export function getVapidPublicKey(): string | undefined {
    return process.env.VAPID_PUBLIC_KEY;
}

function generateQuickChoiceToken(login: string): string {
    const today = new Date().toISOString().slice(0, 10);
    const secret = process.env.JWT_SECRET ?? '';
    return crypto.createHmac('sha256', secret).update(`${login}:${today}`).digest('hex');
}

/** Ověří jednorázový token z push notifikace. */
export function verifyQuickChoiceToken(login: string, token: string): boolean {
    if (!login || !token || token.length !== 64) return false;
    const expected = generateQuickChoiceToken(login);
    return crypto.timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(token, 'hex'));
}


/** Zkontroluje a odešle připomínky uživatelům, kteří si nezvolili oběd. */
async function checkAndSendReminders(): Promise<void> {
    // Přeskočit víkendy
    if (getIsWeekend(getToday())) {
        return;
    }

    const registry = await getRegistry();
    const entries = Object.entries(registry);
    if (entries.length === 0) {
        return;
    }

    const currentTime = getCurrentTimeHHMM();

    // Získáme data pro dnešek jednou pro všechny uživatele
    let clientData;
    try {
        clientData = await getClientData(getToday());
    } catch (e) {
        console.error('Push reminder: chyba při získávání dat', e);
        return;
    }

    for (const [login, entry] of entries) {
        // Ještě nedosáhl čas připomínky
        if (currentTime < entry.time) {
            continue;
        }

        // Cooldown — nepřipomínat častěji než jednou za hodinu
        const last = lastReminded.get(login) ?? 0;
        if (Date.now() - last < REMINDER_COOLDOWN_MS) {
            continue;
        }

        // Uživatel už má zvolenou možnost
        if (clientData.choices && userHasChoice(clientData.choices, login)) {
            continue;
        }

        // Odešleme push notifikaci
        try {
            await webpush.sendNotification(
                entry.subscription,
                JSON.stringify({
                    title: 'Luncher',
                    body: 'Ještě nemáte zvolený oběd!',
                    login,
                    token: generateQuickChoiceToken(login),
                })
            );
            lastReminded.set(login, Date.now());
            console.log(`Push reminder: odeslána připomínka uživateli ${login}`);
        } catch (error: any) {
            if (error.statusCode === 410 || error.statusCode === 404) {
                // Subscription expirovala nebo je neplatná — odebereme z registry
                console.log(`Push reminder: subscription uživatele ${login} expirovala, odebírám`);
                delete registry[login];
                await saveRegistry(registry);
            } else {
                console.error(`Push reminder: chyba při odesílání notifikace uživateli ${login}:`, error);
            }
        }
    }
}

/** Spustí scheduler pro kontrolu a odesílání připomínek každou minutu. */
export function startReminderScheduler(): void {
    const publicKey = process.env.VAPID_PUBLIC_KEY;
    const privateKey = process.env.VAPID_PRIVATE_KEY;
    const subject = process.env.VAPID_SUBJECT;

    if (!publicKey || !privateKey || !subject) {
        console.log('Push reminder: VAPID klíče nejsou nastaveny, scheduler nebude spuštěn');
        return;
    }

    webpush.setVapidDetails(subject, publicKey, privateKey);

    // Spustíme kontrolu každou minutu
    setInterval(checkAndSendReminders, 60_000);
    console.log('Push reminder: scheduler spuštěn');
}
