import { dom, qs$ } from './dom.js';
import { deepT } from './deepblocker-i18n.js';
import {
    getDeepBlockerLanguage,
    getPresetState,
    isRegionalList,
    regionKeyForLanguage,
} from './deepblocker-presets.js';

/******************************************************************************/

const uiStateKeys = [
    'dashboardLastVisitedPane',
    'expandedListSet',
    'firewallFilters',
    'hideUnusedFilterLists',
    'popupExpandExceptions',
    'popupFontSize',
    'popupPanelSections',
];

const formatNumber = value =>
    typeof value === 'number' ? value.toLocaleString() : '-';

const setText = (selector, text) => {
    const node = qs$(selector);
    if ( node !== null ) {
        node.textContent = text;
    }
};

const setStatusCard = (selector, status) => {
    const node = qs$(selector);
    if ( node === null ) { return; }
    node.closest('.deep-diagnostic-card')?.classList.remove('ok', 'warn', 'fail');
    node.closest('.deep-diagnostic-card')?.classList.add(status);
};

const renderChecks = async (selector, checks) => {
    const parent = qs$(selector);
    if ( parent === null ) { return; }
    parent.textContent = '';
    const readyText = await deepT('ready');
    const missingText = await deepT('missing');
    for ( const check of checks ) {
        const row = document.createElement('div');
        row.className = `deep-check ${check.ok ? 'ok' : 'fail'}`;
        const name = document.createElement('span');
        name.textContent = check.name;
        const value = document.createElement('span');
        value.textContent = check.ok ? readyText : missingText;
        row.append(name, value);
        parent.append(row);
    }
};

const showNotice = text => {
    const notice = qs$('#diagnosticsNotice');
    if ( notice === null ) { return; }
    notice.textContent = text;
    notice.hidden = text === '';
};

const renderDiagnostics = async details => {
    const engineReady = details.readyToFilter === true;
    const filtersReady = details.filterEngineReady === true;

    setText('#diagnosticsSummary',
        engineReady && filtersReady
            ? await deepT('diagnosticsRunning')
            : await deepT('diagnosticsStarting')
    );
    setText('#engineStatus', engineReady ? await deepT('ready') : await deepT('starting'));
    setText('#engineDetail',
        engineReady
            ? await deepT('engineReadyDesc')
            : await deepT('engineStartingDesc')
    );
    setStatusCard('#engineStatus', engineReady ? 'ok' : 'warn');

    setText('#filterStatus', filtersReady ? await deepT('loaded') : await deepT('needsAttention'));
    setText('#enabledListCount', formatNumber(details.enabledListCount));
    setText('#filterListCount', formatNumber(details.filterListCount));
    setStatusCard('#filterStatus', filtersReady ? 'ok' : 'warn');

    setText('#netFilterCount', formatNumber(details.netFilterCount));
    setText('#cosmeticFilterCount', formatNumber(details.cosmeticFilterCount));
    setStatusCard('#netFilterCount', details.netFilterCount > 0 ? 'ok' : 'warn');
    setStatusCard('#cosmeticFilterCount', details.cosmeticFilterCount > 0 ? 'ok' : 'warn');

    setText('#manifestVersion', `Manifest V${details.manifestVersion || 2}`);
    setText('#popupStatsStatus', details.popupStatsStatus || '-');

    await renderChecks('#runtimeChecks', [
        { name: await deepT('runtimeOpenTabs'), ok: details.runtimeApis?.openNewTab === true },
        { name: await deepT('runtimeCurrentTab'), ok: details.runtimeApis?.tabsGetCurrent === true },
        { name: await deepT('runtimePageStats'), ok: details.runtimeApis?.pageStoreFromTabId === true },
    ]);

    await renderChecks('#permissionChecks', [
        { name: await deepT('tabsPermission'), ok: details.permissions?.tabs === true },
        { name: await deepT('privacyPermission'), ok: details.permissions?.privacy === true },
        { name: await deepT('webRequestPermission'), ok: details.permissions?.webRequest === true },
        { name: await deepT('webNavigationPermission'), ok: details.permissions?.webNavigation === true },
    ]);

    showNotice(details.manifestVersion === 2
        ? await deepT('mv2Expected')
        : ''
    );
};

const isStockList = details =>
    details instanceof Object &&
    details.content === 'filters' &&
    details.external !== true &&
    details.group !== 'user' &&
    details.group !== 'custom';

const presetLabel = async state => {
    if ( state.preset === 'custom' ) {
        return deepT('customPreset');
    }
    const baseName = await deepT(state.preset);
    if ( state.modified === true ) {
        return deepT('modifiedFromPreset', { preset: baseName });
    }
    return baseName;
};

const loadFilterHealth = async ( ) => {
    let listsetDetails;
    try {
        listsetDetails = await vAPI.messaging.send('dashboard', { what: 'getLists' });
    } catch {
        listsetDetails = undefined;
    }
    if (
        listsetDetails instanceof Object === false ||
        listsetDetails.available instanceof Object === false
    ) {
        return;
    }
    const available = listsetDetails.available;
    const cache = listsetDetails.cache || {};
    const isObsolete = (key, details) => {
        const cacheEntry = cache[key];
        if ( cacheEntry instanceof Object && cacheEntry.obsolete !== undefined ) {
            return cacheEntry.obsolete === true;
        }
        return details.obsolete === true;
    };

    let enabled = 0;
    let totalStock = 0;
    let stale = 0;
    for ( const [ key, details ] of Object.entries(available) ) {
        if ( isStockList(details) === false ) { continue; }
        totalStock += 1;
        const isOn = details.off !== true;
        if ( isOn ) {
            enabled += 1;
            if ( isObsolete(key, details) ) { stale += 1; }
        }
    }

    setText('#fhEnabled', `${enabled.toLocaleString()} / ${totalStock.toLocaleString()}`);
    setText('#fhStale',
        stale === 0
            ? await deepT('filtersAreUpToDate')
            : `${stale.toLocaleString()} · ${await deepT('updateRecommended')}`
    );

    // Active preset
    let presetState;
    try {
        presetState = await getPresetState(listsetDetails);
        setText('#fhPreset', await presetLabel(presetState));
    } catch {
        presetState = undefined;
        setText('#fhPreset', '-');
    }

    // Quick fixes / video resources
    const quickFix = available['ublock-quick-fixes'];
    let quickFixStatus = 'unavailable';
    if ( quickFix instanceof Object === false ) {
        quickFixStatus = 'unavailable';
        setText('#fhQuickFix', await deepT('unavailable'));
    } else if ( quickFix.off === true ) {
        quickFixStatus = 'disabled';
        setText('#fhQuickFix', await deepT('disabled'));
    } else if ( isObsolete('ublock-quick-fixes', quickFix) ) {
        quickFixStatus = 'stale';
        setText('#fhQuickFix', await deepT('updateRecommended'));
    } else {
        quickFixStatus = 'fresh';
        setText('#fhQuickFix', await deepT('filtersAreUpToDate'));
    }

    // Regional protection for the selected DeepBlocker language
    const language = await getDeepBlockerLanguage();
    const regionKey = regionKeyForLanguage(available, language);
    let regionState = 'global';
    if ( regionKey === '' ) {
        regionState = 'global';
        setText('#fhRegional', await deepT('regionalGlobal'));
    } else {
        const regionOn = available[regionKey] && available[regionKey].off !== true;
        regionState = regionOn ? 'active' : 'available';
        setText('#fhRegional', regionOn
            ? await deepT('regionalActive')
            : await deepT('regionalAvailable')
        );
    }

    // Last update time
    const writeTimes = Object.values(listsetDetails.cache || {})
        .map(asset => asset && asset.writeTime || 0)
        .filter(time => time > 0);
    setText('#fhLastUpdate', writeTimes.length !== 0
        ? new Date(Math.max(...writeTimes)).toLocaleString()
        : await deepT('neverRecorded')
    );
};

const loadDiagnostics = async ( ) => {
    setText('#diagnosticsSummary', await deepT('diagnosticsChecking'));
    try {
        const details = await vAPI.messaging.send('dashboard', {
            what: 'getDiagnostics',
        });
        if ( details instanceof Object === false ) {
            throw new Error('Diagnostics data is unavailable');
        }
        await renderDiagnostics(details);
    } catch (ex) {
        setText('#diagnosticsSummary', await deepT('diagnosticsUnavailable'));
        setText('#engineStatus', await deepT('unavailable'));
        setText('#filterStatus', await deepT('unavailable'));
        setStatusCard('#engineStatus', 'fail');
        setStatusCard('#filterStatus', 'fail');
        showNotice(await deepT('diagnosticsStillStarting'));
        console.warn('DeepBlocker diagnostics failed', ex);
    }
    try {
        await loadFilterHealth();
    } catch (ex) {
        console.warn('DeepBlocker filter health failed', ex);
    }
};

const updateFilters = async ( ) => {
    const button = qs$('#updateFilters');
    button?.classList.add('disabled');
    setText('#diagnosticsSummary', await deepT('startingUpdate'));
    try {
        await vAPI.messaging.send('dashboard', { what: 'updateNow' });
        setText('#diagnosticsSummary', await deepT('updateStartedRefresh'));
    } catch (ex) {
        showNotice(await deepT('updateCouldNotStart'));
        console.warn('DeepBlocker diagnostic update failed', ex);
    } finally {
        button?.classList.remove('disabled');
    }
};

const resetUiState = async ( ) => {
    if ( window.confirm(await deepT('resetUiConfirm')) !== true ) {
        return;
    }
    await Promise.all(uiStateKeys.map(key => vAPI.localStorage.removeItem(key)));
    showNotice(await deepT('uiStateReset'));
};

dom.on('#refreshDiagnostics', 'click', loadDiagnostics);
dom.on('#updateFilters', 'click', updateFilters);
dom.on('#resetUiState', 'click', resetUiState);

loadDiagnostics();

/******************************************************************************/
