// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'chrome://resources/cr_elements/cr_tab_box/cr_tab_box.js';
import { assert } from 'chrome://resources/js/assert.js';
import { PageHandler } from './browsing_topics_internals.mojom-webui.js';
let pageHandler = null;
let hostsClassificationSequenceNumber = 0;
function setElementVisible(id, visible) {
    const element = document.querySelector('#' + id);
    element.style.display = visible ? 'block' : 'none';
}
function setButtonEnabled(id, enabled) {
    const element = document.querySelector('#' + id);
    element.disabled = !enabled;
}
function formatTimeDuration(totalMicrosecondsBigInt) {
    if (totalMicrosecondsBigInt > Number.MAX_SAFE_INTEGER) {
        return '+inf';
    }
    const totalMicroseconds = Number(totalMicrosecondsBigInt);
    let totalSeconds = Math.floor(totalMicroseconds / 1000000);
    const days = Math.floor(totalSeconds / 3600 / 24);
    totalSeconds %= 3600 * 24;
    const hours = Math.floor(totalSeconds / 3600);
    totalSeconds %= 3600;
    const minutes = Math.floor(totalSeconds / 60);
    const seconds = Math.floor(totalSeconds % 60);
    return days + 'd-' + hours + 'h-' + minutes + 'm-' + seconds + 's';
}
function formatMojoTime(mojoTime) {
    // The JS Date() is based off of the number of milliseconds since the
    // UNIX epoch (1970-01-01 00::00:00 UTC), while |internalValue| of the
    // base::Time (represented in mojom.Time) represents the number of
    // microseconds since the Windows FILETIME epoch (1601-01-01 00:00:00 UTC).
    // This computes the final JS time by computing the epoch delta and the
    // conversion from microseconds to milliseconds.
    const windowsEpoch = Date.UTC(1601, 0, 1, 0, 0, 0, 0);
    const unixEpoch = Date.UTC(1970, 0, 1, 0, 0, 0, 0);
    // |epochDeltaInMs| equals to base::Time::kTimeTToMicrosecondsOffset.
    const epochDeltaInMs = unixEpoch - windowsEpoch;
    const timeInMs = Number(mojoTime.internalValue) / 1000;
    return (new Date(timeInMs - epochDeltaInMs)).toLocaleString();
}
function getEnabledStatusText(enabled) {
    return enabled ? 'enabled' : 'disabled';
}
function getRealOrRandomStatusText(isRealTopic) {
    return isRealTopic ? 'Real' : 'Random';
}
function createContextDomainEntry(domain) {
    const entry = document
        .querySelector('#context-domain-entry-template').content.cloneNode(true);
    entry.querySelectorAll('span')[0].textContent = domain;
    return entry;
}
function createTopicRow(topic) {
    const row = document.querySelector('#topic-row-template').content.cloneNode(true);
    const nestedCells = row.querySelectorAll('td');
    nestedCells[0].textContent = String(topic.topicId);
    nestedCells[1].textContent = topic.topicName;
    nestedCells[2].textContent = getRealOrRandomStatusText(topic.isRealTopic);
    topic.observedByDomains.forEach((domain) => {
        row.querySelectorAll('td')[3].appendChild(createContextDomainEntry(domain));
    });
    return row;
}
function fieldNameFromId(id) {
    const tokens = id.split('-');
    let processedFirstToken = false;
    const convertedTokens = tokens.map(token => {
        if (!processedFirstToken) {
            processedFirstToken = true;
            return token;
        }
        if (token === 'div') {
            return '';
        }
        // Capitalize the first letter
        return token.charAt(0).toUpperCase() + token.slice(1);
    });
    return convertedTokens.join('');
}
async function asyncGetBrowsingTopicsConfiguration() {
    assert(pageHandler);
    const response = await pageHandler.getBrowsingTopicsConfiguration();
    const config = response.config;
    // Enabled status fields
    ['browsing-topics-enabled-div',
        'privacy-sandbox-ads-apis-override-enabled-div',
        'browsing-topics-bypass-ip-is-publicly-routable-check-enabled-div',
        'browsing-topics-document-api-enabled-div',
        'browsing-topics-parameters-enabled-div']
        .forEach(id => {
        const div = document.querySelector(`#${id}`);
        assert(div);
        div.textContent += getEnabledStatusText(config[fieldNameFromId(id)]);
    });
    // Number fields
    ['config-version-div', 'number-of-epochs-to-expose-div',
        'number-of-top-topics-per-epoch-div',
        'use-random-topic-probability-percent-div',
        'number-of-epochs-of-observation-data-to-use-for-filtering-div',
        'max-number-of-api-usage-context-domains-to-keep-per-topic-div',
        'max-number-of-api-usage-context-entries-to-load-per-epoch-div',
        'max-number-of-api-usage-context-domains-to-store-per-page-load-div',
        'taxonomy-version-div', 'disabled-topics-list-div']
        .forEach(id => {
        const div = document.querySelector(`#${id}`);
        assert(div);
        div.textContent +=
            config[fieldNameFromId(id)];
    });
    // Time duration fields
    ['time-period-per-epoch-div', 'max-epoch-introduction-delay-div'].forEach(id => {
        const div = document.querySelector(`#${id}`);
        assert(div);
        div.textContent += formatTimeDuration(config[fieldNameFromId(id)]
            .microseconds);
    });
}
async function asyncGetBrowsingTopicsState(calculateNow) {
    // Clear and hide existing content.
    document.querySelector('#epoch-div-list-wrapper').innerHTML =
        window.trustedTypes.emptyHTML;
    setElementVisible('topics-state-override-status-message-div', false);
    setElementVisible('topics-state-div', false);
    // Disable the buttons to make sure only one request (either Refresh or
    // Calculate Now) can be made at a time.
    setButtonEnabled('refresh-topics-state-button', false);
    setButtonEnabled('calculate-now-button', false);
    assert(pageHandler);
    const response = await pageHandler.getBrowsingTopicsState(calculateNow);
    setButtonEnabled('refresh-topics-state-button', true);
    setButtonEnabled('calculate-now-button', true);
    const result = response.result;
    if (result.overrideStatusMessage) {
        document.querySelector('#topics-state-override-status-message-div').textContent =
            result.overrideStatusMessage;
        setElementVisible('topics-state-override-status-message-div', true);
        return;
    }
    setElementVisible('topics-state-div', true);
    document.querySelector('#next-scheduled-calculation-time-div').textContent =
        'Next scheduled calculation time: ' +
            formatMojoTime(result.browsingTopicsState.nextScheduledCalculationTime);
    result.browsingTopicsState.epochs.forEach((epoch) => {
        const epochDiv = document.querySelector('#epoch-div-template').content.cloneNode(true);
        const nestedDivs = epochDiv.querySelectorAll('div');
        nestedDivs[1].textContent += formatMojoTime(epoch.calculationTime);
        nestedDivs[2].textContent += epoch.modelVersion;
        nestedDivs[3].textContent += epoch.taxonomyVersion;
        epoch.topics.forEach((topic) => {
            epochDiv.querySelectorAll('table')[0].appendChild(createTopicRow(topic));
        });
        document.querySelector('#epoch-div-list-wrapper').appendChild(epochDiv);
    });
}
function createClassificationResultTopicEntry(topic) {
    const entry = document
        .querySelector('#classification-result-topic-entry-template').content
        .cloneNode(true);
    entry.querySelectorAll('span')[0].textContent = topic;
    return entry;
}
function createClassificationResultRow(host, topics) {
    const row = document
        .querySelector('#classification-result-host-row-template').content
        .cloneNode(true);
    const nestedCells = row.querySelectorAll('td');
    nestedCells[0].textContent = host;
    topics.forEach((topic) => {
        const topicText = String(topic.topicId) + '. ' + topic.topicName;
        nestedCells[1].appendChild(createClassificationResultTopicEntry(topicText));
    });
    return row;
}
async function asyncClassifyHosts(hosts, sequenceNumber) {
    let topicsForHosts = [];
    if (hosts.length > 0) {
        assert(pageHandler);
        const response = await pageHandler.classifyHosts(hosts);
        topicsForHosts = response.topicsForHosts;
    }
    // Skip this result if a newer classification was initiated before this one
    // finished.
    if (sequenceNumber !== hostsClassificationSequenceNumber) {
        return;
    }
    for (let i = 0; i < hosts.length; i++) {
        const host = hosts[i];
        const topics = topicsForHosts[i];
        document.querySelector('#hosts-classification-result-table').appendChild(createClassificationResultRow(host, topics));
    }
    setElementVisible('hosts-classification-loader-div', false);
    setElementVisible('hosts-classification-result-table-wrapper', true);
}
function clearHostsClassificationResult() {
    const table = document.querySelector('#hosts-classification-result-table');
    assert(table);
    while (table.rows[1]) {
        table.deleteRow(1);
    }
    const div = document.querySelector('#hosts-classification-input-validation-error');
    div.innerHTML = window.trustedTypes.emptyHTML;
    setElementVisible('hosts-classification-loader-div', false);
    setElementVisible('hosts-classification-input-validation-error', false);
    setElementVisible('hosts-classification-result-table-wrapper', false);
}
async function asyncGetModelInfo() {
    assert(pageHandler);
    const response = await pageHandler.getModelInfo();
    setElementVisible('model-info-loader', false);
    const result = response.result;
    if (result.overrideStatusMessage) {
        document.querySelector('#model-info-override-status-message-div').textContent =
            result.overrideStatusMessage;
        setElementVisible('model-info-override-status-message-div', true);
        return;
    }
    document.querySelector('#model-version-div').textContent +=
        result.modelInfo.modelVersion;
    document.querySelector('#model-file-path-div').textContent +=
        result.modelInfo.modelFilePath;
    setElementVisible('model-info-div', true);
    setElementVisible('hosts-classification-input-area-div', true);
    document.querySelector('#hosts-classification-button').addEventListener('click', () => {
        clearHostsClassificationResult();
        const input = document.querySelector('#input-hosts-textarea').value;
        const hosts = input.split('\n');
        const preprocessedHosts = [];
        hosts.forEach((host) => {
            const trimmedHost = host.trim();
            if (trimmedHost === '') {
                return;
            }
            preprocessedHosts.push(trimmedHost);
        });
        const inputValidationErrors = [];
        preprocessedHosts.forEach((host) => {
            if (host.includes('/')) {
                inputValidationErrors.push('Host \"' + host + '" contains invalid character: "\/"');
            }
        });
        ++hostsClassificationSequenceNumber;
        if (inputValidationErrors.length > 0) {
            const errorsDiv = document.querySelector('#hosts-classification-input-validation-error');
            inputValidationErrors.forEach((error) => {
                const errorDiv = document.createElement('div');
                errorDiv.textContent = error;
                errorsDiv.appendChild(errorDiv);
            });
            setElementVisible('hosts-classification-input-validation-error', true);
        }
        else {
            setElementVisible('hosts-classification-loader-div', true);
            asyncClassifyHosts(preprocessedHosts, hostsClassificationSequenceNumber);
        }
    });
}
document.addEventListener('DOMContentLoaded', function () {
    // Setup the mojo interface.
    pageHandler = PageHandler.getRemote();
    setElementVisible('topics-state-override-status-message-div', false);
    setElementVisible('topics-state-div', false);
    setElementVisible('model-info-override-status-message-div', false);
    setElementVisible('model-info-div', false);
    setElementVisible('hosts-classification-input-area-div', false);
    setElementVisible('hosts-classification-loader-div', false);
    setElementVisible('hosts-classification-input-validation-error', false);
    setElementVisible('hosts-classification-result-table-wrapper', false);
    asyncGetBrowsingTopicsConfiguration();
    asyncGetModelInfo();
    document.querySelector('#refresh-topics-state-button').addEventListener('click', () => {
        asyncGetBrowsingTopicsState(/*calculateNow=*/ false);
    });
    document.querySelector('#calculate-now-button').addEventListener('click', () => {
        asyncGetBrowsingTopicsState(/*calculateNow=*/ true);
    });
    asyncGetBrowsingTopicsState(/*calculateNow=*/ false);
});
