import { ToubizLegacyParser } from '@nimius/toubiz-opening-times-calculation';
import { deepGet } from '@nimius/object-utility';

/**
 * List of parsers.
 * Use this object to add additional parsers (e.g. for the new toubiz format)
 * in the future.
 *
 * @type {object<string, AbstractParser>}
 */
const parsers = { legacy: new ToubizLegacyParser() };

/**
 * @param {string} parserName
 * @returns {AbstractParser}
 */
function getParser(parserName) {
    const parser = parsers[parserName];
    if (!parser) {
        throw new Error(`
            No opening times parser with the name ${parserName} found.
            Available parsers:${Object.keys(parsers).join(', ')}
        `);
    }
    return parser;
}

/**
 * @param {HTMLElement} node
 * @param {OpeningTimesCollection~TimesAtReturn} result
 */
function toggleElementsBasedOnOpenState(node, result) {
    for (const element of node.querySelectorAll('[data-toubiz-opening-times\\.show-if-open]')) {
        element.style.display = result.isOpen ? '' : 'none';
    }
    for (const element of node.querySelectorAll('[data-toubiz-opening-times\\.show-if-closed]')) {
        element.style.display = result.isOpen ? 'none' : '';
    }
}

/**
 * @param {HTMLElement} node
 * @param {OpeningTimesCollection~TimesAtReturn} result
 */
function fillChildrenWithContentsOfResult(node, result) {
    for (const element of node.querySelectorAll('[data-toubiz-opening-times\\.fill]')) {
        const key = element.getAttribute('data-toubiz-opening-times.fill');
        const prepend = element.getAttribute('data-toubiz-opening-times.fill.prepend') || '';
        const append = element.getAttribute('data-toubiz-opening-times.fill.append') || '';
        const value = deepGet(result, key);

        if (value) {
            element.innerText = `${prepend}${value}${append}`;
        } else {
            element.style.display = 'none';
        }
    }
}

/**
 * ## Opening Times Resolution
 * Resolves the given opening times rules and checks if a point is currently open.
 *
 * There are 2 sets of actions that this script performs with the results:
 * 1. Show or Hide results based on the open state
 * 2. Fill children containers with strings from the result.
 *
 * ### Attributes on wrapper
 * - `data-toubiz-opening-times`: Must be set on a wrapping element and contain the JSON structure
 *   of the Opening times that should be parsed.
 * - `data-toubiz-opening-times.parser`: May be set on the same element in order to select a differing
 *   parser for the opening times. Currently only one parser exists: `legacy`. Defaults to `legacy`.
 * - `data-toubiz-opening-times.date`: Date and time at which the opening times should be checked.
 *   The given date must be ISO8601 formatted in order to prevent parsing flaws of different browsers.
 *   Defaults to the current date and time if not set.
 *
 * ### Attributes / Behaviour on children
 * - `data-toubiz-opening-times.show-if-open`: Displays the element if the point is open, Hides it, if it is closed.
 * - `data-toubiz-opening-times.show-if-open`: Displays the element if the point is closed, Hides it, if it is open.
 * - `data-toubiz-opening-times.fill`: Fill the element with an attribute of the resulting opening times
 *   resolution. If no attribute with the given name was found then the element will be hidden.
 *   The available attributes correspond with the return value of OpeningTimesCollection#timesAt:
 *   https://git.nimius.be/packages/toubiz-opening-times-calculation/#OpeningTimesCollection..TimesAtReturn
 *
 * @example
 * <p class="tb-hours-now" data-toubiz-opening-times="{point.opentimes}">
 *      <strong class="font-bold text-positive" data-toubiz-opening-times.show-if-open>
 *          Geöffnet
 *      </strong>
 *      <span data-toubiz-opening-times.fill="timeRange.end"
 *            data-toubiz-opening-times.fill.prepend="bis "></span>
 *
 *      <strong class="font-bold text-negative" data-toubiz-opening-times.show-if-closed>
 *          Geschlossen
 *      </strong>
 *      <span data-toubiz-opening-times.fill="nextTimeRange.beginning"
 *            data-toubiz-opening-times.fill.prepend="bis "></span>
 * </p>
 *
 * @param {HTMLElement} node
 */
export default function parseOpeningTimes(node) {
    const parserName = node.getAttribute('data-toubiz-opening-times.parser') || 'legacy';
    const parser = getParser(parserName);

    const openingTimes = parser.parse(JSON.parse(node.getAttribute('data-toubiz-opening-times')));
    const parseAt = node.getAttribute('data-toubiz-opening-times.date') || (new Date()).toISOString();
    const result = openingTimes.timesAt(new Date(parseAt));

    toggleElementsBasedOnOpenState(node, result);
    fillChildrenWithContentsOfResult(node, result);
}
