// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import { assert } from 'chrome://resources/js/assert.js';
import { CustomElement } from 'chrome://resources/js/custom_element.js';
import { getTemplate } from './app.html.js';
import { StructuredMetricsBrowserProxyImpl } from './structured_metrics_browser_proxy.js';
import { updateStructuredMetricsEvents, updateStructuredMetricsSummary } from './structured_utils.js';
/**
 * Gets search params from search url.
 */
function getSearchParams() {
    return window.location.search.substring(1);
}
export class StructuredMetricsInternalsAppElement extends CustomElement {
    static get is() {
        return 'structured-metrics-internals-app';
    }
    static get template() {
        return getTemplate();
    }
    browserProxy_ = StructuredMetricsBrowserProxyImpl.getInstance();
    summaryIntervalId_;
    initPromise;
    textSearch;
    searchQuery = null;
    searchError = false;
    constructor() {
        super();
        // This must be set before |initSearchParams_| is called.
        this.textSearch = this.getRequiredElement('#search-bar');
        this.initSearchParams_();
        this.initPromise = this.init_();
        // Create periodic callbacks.
        this.summaryIntervalId_ =
            setInterval(() => this.updateStructuredMetricsSummary_(), 5000);
    }
    disconnectedCallback() {
        clearInterval(this.summaryIntervalId_);
    }
    async init_() {
        // Fetch Structured Metrics summary and events.
        // TODO: Implement a push model as new events are recorded.
        await this.updateStructuredMetricsSummary_();
        await this.updateStructuredMetricsEvents_();
        const eventRefreshButton = this.getRequiredElement('#sm-refresh-events');
        eventRefreshButton.addEventListener('click', () => this.updateStructuredMetricsEvents_());
    }
    /**
     * Fetches summary information of the Structured Metrics service and renders
     * it.
     */
    async updateStructuredMetricsSummary_() {
        const summary = await this.browserProxy_.fetchStructuredMetricsSummary();
        const template = this.getRequiredElement('#summary-row-template');
        const smSummaryBody = this.getRequiredElement('#sm-summary-body');
        updateStructuredMetricsSummary(smSummaryBody, summary, template);
    }
    /**
     * Fetches all events currently recorded by the Structured Metrics Service and
     * renders them. It an event has been uploaded then it will not be shown
     * again. This only shows Events recorded in Chromium. Platform2 events are
     * not supported yet.
     */
    async updateStructuredMetricsEvents_() {
        const events = await this.browserProxy_.fetchStructuredMetricsEvents();
        const eventTemplate = this.getRequiredElement('#structured-metrics-event-row-template');
        const eventDetailsTemplate = this.getRequiredElement('#structured-metrics-event-details-template');
        const kvTemplate = this.getRequiredElement('#summary-row-template');
        const eventTableBody = this.getRequiredElement('#sm-events-body');
        updateStructuredMetricsEvents(eventTableBody, events, this.searchQuery, eventTemplate, eventDetailsTemplate, kvTemplate);
    }
    /**
     * Initializes search params from the URL.
     */
    initSearchParams_() {
        const searchString = this.sanitizeUrlToSearch_();
        this.searchQuery = this.parseSearchString_(searchString);
        this.textSearch.value = searchString;
        this.textSearch.addEventListener('search', () => {
            this.updateSearchCriteria_();
        });
    }
    /**
     * Updates the windows search url.
     */
    updateSearchCriteria_() {
        // Update the url to reflect the search string. This will redirect the new
        // url page, updating the searchQuery.
        window.location.search = '?' + this.sanitizeSearchToUrl_();
    }
    /**
     * Sanitize the search format into a valid format for the URL.
     */
    sanitizeSearchToUrl_() {
        return this.textSearch.value.replace(/\s+/gi, '&').replace(/:/gi, '=');
    }
    /**
     * Sanitize the URL search parameters into the search format.
     */
    sanitizeUrlToSearch_() {
        return getSearchParams().replace(/&/gi, ' ').replace(/=/gi, ':');
    }
    /**
     * Parse search format into an object.
     *
     * The format is a space separated lists of "key:value" pairs. Currently, a
     * single search term is not supported.
     */
    parseSearchString_(text) {
        // Page should be rebuilt on a new search query, but leaving it because it
        // is better to be safe then have an error message that doesn't disappear
        // when the page is refreshed..
        if (this.searchError) {
            this.hideSearchErrorMessage_();
        }
        if (text.length === 0) {
            return null;
        }
        // If an ':' is not found then we are doing a full text search. The string
        // is the query as is.
        if (text.indexOf(':') === -1) {
            return null;
        }
        // If it is found, then we are doing a categorical search, this parses the
        // string into a map of category and search value.
        const categories = new Map();
        const validSearchKeys = ['project', 'event', 'metric'];
        text.split(' ').forEach((cat) => {
            const [key, value] = cat.split(':', 2);
            if (key !== undefined && value !== undefined) {
                if (validSearchKeys.find((value) => value === key) === undefined) {
                    this.setSearchErrorMessage_(`invalid search key: ${key}. valid keys are project, event, metric`);
                    return;
                }
                categories.set(key, value);
            }
        });
        return categories;
    }
    /**
     * Hides the search error message.
     */
    hideSearchErrorMessage_() {
        this.searchError = false;
        const errorMsg = this.getRequiredElement('#search-error-msg');
        assert(errorMsg);
        errorMsg.style.display = 'none';
    }
    /**
     * Sets and shows the error message.
     */
    setSearchErrorMessage_(msg) {
        this.searchError = true;
        // Set the content of the error message.
        const errorMsg = this.getRequiredElement('#search-error-msg');
        assert(errorMsg);
        errorMsg.innerText = msg;
        // Shows the error message
        errorMsg.style.display = 'block';
    }
}
customElements.define(StructuredMetricsInternalsAppElement.is, StructuredMetricsInternalsAppElement);
