"use strict";

window.CLIENT = window.CLIENT || null;
window.CLIENTS = window.CLIENTS || {};

function getStoredUsername(client) {
    return window.localStorage.getItem(`${CLIENT}/${client}/username`);
}

function getStoredToken(client) {
    return window.localStorage.getItem(`${CLIENT}/${client}/token`);
}

function getAuthorizationHeader(client) {
    return {
        'Authorization': 'Bearer ' + window.localStorage.getItem(`${CLIENT}/${client}/token`),
    };
}

async function authenticate(client, username, password) {
    const res = await fetch(`${CLIENTS[client]['api']}/auth`, {
        method: 'GET',
        headers: {'Authorization': 'Basic ' + btoa(username + ':' + password)},
    });
    const json = await res.json();
    if (!res.ok) throw new ApiError(res.status, json['message']);
    return json['token'];
}

async function get(client, path) {
    const res = await fetch(`${CLIENTS[client]['api']}${path}`, {
        method: 'GET',
        headers: {...getAuthorizationHeader(client)},
    });
    const json = await res.json();
    if (!res.ok) throw new ApiError(res.status, json['message']);
    return json;
}

async function getDeliverySchedules(client, filters, limit, offset) {
    const query = [];
    if (!!filters) query.push(`filters=${filters.join(',')}`);
    if (!!limit) query.push(`limit=${limit}`);
    if (!!offset) query.push(`offset=${offset}`);
    return await get(client, `/delivery_schedules${!!query ? '?' : ''}${query.join('&')}`);
}

async function init() {
    render();
}

async function updateOverview(client) {
    const [schedules] = await Promise.all([getDeliverySchedules(client, [`year=${getCurrentLastSeason()}`])]);
    const rows = [];
    const days = groupBy(schedules['data'], 'date');
    const now = new Date();
    for (const [dateString, day] of Object.entries(days)) {
        const date = new Date(dateString);
        const row = document.createElement('div');
        row.className = 'day';
        if (now.getFullYear() === date.getFullYear() && now.getMonth() === date.getMonth() && now.getDate() === date.getDate())
            row.classList.add('today');
        row.innerHTML = `<div><span style="font-size: 0.75em; display: block">${fmtDateWeekday(date)}</span>${fmtDate(date)}</div>`;
        const container = document.createElement('div');
        container.className = 'schedule-container';
        for (const schedule of day) {
            const from = schedule.announcement_from !== null ? new Date(schedule.announcement_from) : null;
            const to = schedule.announcement_to !== null ? new Date(schedule.announcement_to) : null;
            const status = from === null && to === null ? 'Anmeldung offen' : from > now ? `Anmeldung ab ${fmtDateTime(from)}` : to > now ? `Anmeldung bis ${fmtDateTime(new Date(to - 1))}` : 'Anmeldefrist vorbei';
            const link = document.createElement('a');
            if (schedule.is_cancelled) link.className = 'cancelled';
            link.innerHTML += `<div><span style="font-size: 0.75em; display: block;">${escapeHTML(schedule.branch.name)}</span><span>${escapeHTML(schedule.description)}</span><span style="font-size: 0.75em; display: block;">${status}</span></div>`;
            if (schedule.delivered_weight > 0) {
                link.innerHTML += `
                    <span>
                      <span><strong>${fmtInt(schedule.delivered_weight)} kg</strong></span> /
                      <span class="min-kg">${fmtInt(schedule.announced_weight)} kg</span>
                      (<span class="min-percent">${fmtInt(Math.round(schedule.delivered_weight / schedule.announced_weight * 100))}%</span>)
                    </span>`;
            } else if (schedule.max_weight !== null) {
                link.innerHTML += `
                    <span>
                      <span>${fmtInt(schedule.announced_weight)} kg</span> /
                      <span class="min-kg">${fmtInt(schedule.max_weight)} kg</span>
                      (<span class="min-percent">${fmtInt(Math.round(schedule.announced_weight / schedule.max_weight * 100))}%</span>)
                    </span>`;
            } else {
                link.innerHTML += `<span><span>${fmtInt(schedule.announced_weight)} kg</span></span>`;
            }
            container.append(link);
        }
        row.appendChild(container);
        rows.push(row);
    }

    const main = document.getElementsByTagName('main')[0];
    main.replaceChildren(main.firstElementChild, ...rows);
}

function render() {
    const hash = window.location.hash;
    const main = document.getElementById("access");
    const nav = document.getElementsByTagName("nav")[0].getElementsByTagName("ul")[0];
    for (const li of nav.children) li.className = '';

    const client = Object.keys(CLIENTS).find(id => hash.startsWith(`#/${id}/`) || hash === `#/${id}`);
    if (client === undefined) {
        window.location.hash = `#/${Object.keys(CLIENTS).find(id => !!getStoredUsername(id) && !!getStoredToken(id)) || Object.keys(CLIENTS)[0]}`;
        return;
    }
    nav.children[Object.keys(CLIENTS).indexOf(client)].className = 'active';

    if ((!getStoredUsername(client) || !getStoredToken(client)) && window.location.hash !== `#/${client}/login`) {
        window.location.hash = `#/${client}/login`;
        return;
    }

    if (hash === `#/${client}/login`) {
        main.className = 'login';
        main.innerHTML = `
            <form onsubmit="return actionLogin(this);">
              <h1>Anmelden</h1>
              <input type="text" name="username" placeholder="Benutzername" value="${getStoredUsername(client) ?? ''}"/>
              <input type="password" name="password" placeholder="Kennwort"/>
              <input type="hidden" name="client" value="${client}"/>
              <button type="submit">Anmelden</button>
            </form>`;
    } else if (hash === `#/${client}`) {
        main.className = 'overview';
        main.innerHTML = `
            <h1>${CLIENTS[client].name}</h1>`;
        update();
    } else {
        window.location.hash = `#/${client}`;
    }
}

function update() {
    const hash = window.location.hash;
    const client = Object.keys(CLIENTS).find(id => hash.startsWith(`#/${id}/`) || hash === `#/${id}`);
    if (document.hidden || client === undefined) {
        // do nothing
    } else {
        if (hash === `#/${client}`) {
            updateOverview(client)
                .then()
                .catch(e => {
                    if (e instanceof ApiError && e.statusCode === 401) {
                        window.localStorage.removeItem(`${CLIENT}/${client}/token`);
                        window.location.hash = `#/${client}/login`;
                    } else {
                        alert(e.localizedMessage ?? ERROR_MESSAGES[e.message] ?? `Unbekannter Fehler: ${e.message}`);
                    }
                });
        }
    }
}

document.addEventListener('DOMContentLoaded', async () => {
    await init();
    setInterval(update, 60_000);
});

window.addEventListener('hashchange', render);

window.addEventListener('pageshow', update)

document.addEventListener('visibilitychange', update);

function actionLogin(form) {
    const elements = form.getElementsByClassName('error');
    for (const e of elements) form.removeChild(e);

    const client = form['client'].value;
    window.localStorage.setItem(`${CLIENT}/${client}/username`, form['username'].value);

    authenticate(client, form['username'].value, form['password'].value)
        .then(token => {
            window.localStorage.setItem(`${CLIENT}/${client}/token`, token);
            window.location.hash = `#/${client}`;
        }).catch(e => {
            const error = document.createElement('div');
            error.className = 'error';
            error.innerText = e.localizedMessage ?? ERROR_MESSAGES[e.message] ?? `Unbekannter Fehler\n(${e.message})`;
            form.insertBefore(error, form.lastChild.previousSibling);
        });

    return false;
}