import { __esDecorate, __runInitializers } from "tslib";
import { ErrorResponse } from '@api-client/core/models/ErrorResponse.js';
import { html, nothing } from 'lit';
import { property, state } from 'lit/decorators.js';
import ApiElement from '../ApiElement.js';
import { relativeDay } from '../../lib/time/Conversion.js';
import { HttpHistoryChart } from './HttpHistoryChart.js';
import { statusTemplate } from '../http/HttpStatus.js';
import '@github/relative-time-element';
import '../../define/http/http-request-log.js';
import '../../md/icons/ui-icon.js';
import '../../md/button/ui-button.js';
import '../../md/dropdown-list/ui-dropdown-list.js';
import '../../md/list/ui-list.js';
import '../../md/list/ui-list-item.js';
import '../../md/divider/ui-divider.js';
import '../../md/segmented-button/ui-segmented-button.js';
import '../../md/segmented-button/ui-segmented-button-set.js';
import '../../md/dialog/ui-dialog.js';
export var ProjectHistoryView;
(function (ProjectHistoryView) {
    ProjectHistoryView["history"] = "history";
    ProjectHistoryView["analysis"] = "analysis";
})(ProjectHistoryView || (ProjectHistoryView = {}));
let HttpProjectRequestHistory = (() => {
    let _classSuper = ApiElement;
    let _history_decorators;
    let _history_initializers = [];
    let _history_extraInitializers = [];
    let _project_decorators;
    let _project_initializers = [];
    let _project_extraInitializers = [];
    let _request_decorators;
    let _request_initializers = [];
    let _request_extraInitializers = [];
    let _querying_decorators;
    let _querying_initializers = [];
    let _querying_extraInitializers = [];
    let _model_decorators;
    let _model_initializers = [];
    let _model_extraInitializers = [];
    let _selectedItem_decorators;
    let _selectedItem_initializers = [];
    let _selectedItem_extraInitializers = [];
    let _renderedItem_decorators;
    let _renderedItem_initializers = [];
    let _renderedItem_extraInitializers = [];
    let _view_decorators;
    let _view_initializers = [];
    let _view_extraInitializers = [];
    return class HttpProjectRequestHistory extends _classSuper {
        static {
            const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
            _history_decorators = [state()];
            _project_decorators = [property({ type: String })];
            _request_decorators = [property({ type: String })];
            _querying_decorators = [state()];
            _model_decorators = [state()];
            _selectedItem_decorators = [state()];
            _renderedItem_decorators = [state()];
            _view_decorators = [state()];
            __esDecorate(this, null, _history_decorators, { kind: "accessor", name: "history", static: false, private: false, access: { has: obj => "history" in obj, get: obj => obj.history, set: (obj, value) => { obj.history = value; } }, metadata: _metadata }, _history_initializers, _history_extraInitializers);
            __esDecorate(this, null, _project_decorators, { kind: "accessor", name: "project", static: false, private: false, access: { has: obj => "project" in obj, get: obj => obj.project, set: (obj, value) => { obj.project = value; } }, metadata: _metadata }, _project_initializers, _project_extraInitializers);
            __esDecorate(this, null, _request_decorators, { kind: "accessor", name: "request", static: false, private: false, access: { has: obj => "request" in obj, get: obj => obj.request, set: (obj, value) => { obj.request = value; } }, metadata: _metadata }, _request_initializers, _request_extraInitializers);
            __esDecorate(this, null, _querying_decorators, { kind: "accessor", name: "querying", static: false, private: false, access: { has: obj => "querying" in obj, get: obj => obj.querying, set: (obj, value) => { obj.querying = value; } }, metadata: _metadata }, _querying_initializers, _querying_extraInitializers);
            __esDecorate(this, null, _model_decorators, { kind: "accessor", name: "model", static: false, private: false, access: { has: obj => "model" in obj, get: obj => obj.model, set: (obj, value) => { obj.model = value; } }, metadata: _metadata }, _model_initializers, _model_extraInitializers);
            __esDecorate(this, null, _selectedItem_decorators, { kind: "accessor", name: "selectedItem", static: false, private: false, access: { has: obj => "selectedItem" in obj, get: obj => obj.selectedItem, set: (obj, value) => { obj.selectedItem = value; } }, metadata: _metadata }, _selectedItem_initializers, _selectedItem_extraInitializers);
            __esDecorate(this, null, _renderedItem_decorators, { kind: "accessor", name: "renderedItem", static: false, private: false, access: { has: obj => "renderedItem" in obj, get: obj => obj.renderedItem, set: (obj, value) => { obj.renderedItem = value; } }, metadata: _metadata }, _renderedItem_initializers, _renderedItem_extraInitializers);
            __esDecorate(this, null, _view_decorators, { kind: "accessor", name: "view", static: false, private: false, access: { has: obj => "view" in obj, get: obj => obj.view, set: (obj, value) => { obj.view = value; } }, metadata: _metadata }, _view_initializers, _view_extraInitializers);
            if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
        }
        #history_accessor_storage = __runInitializers(this, _history_initializers, []
        /**
         * The key of the project the request belongs to.
         */
        );
        /**
         * The history list to render.
         */
        get history() { return this.#history_accessor_storage; }
        set history(value) { this.#history_accessor_storage = value; }
        #project_accessor_storage = (__runInitializers(this, _history_extraInitializers), __runInitializers(this, _project_initializers, void 0));
        /**
         * The key of the project the request belongs to.
         */
        get project() { return this.#project_accessor_storage; }
        set project(value) { this.#project_accessor_storage = value; }
        #request_accessor_storage = (__runInitializers(this, _project_extraInitializers), __runInitializers(this, _request_initializers, void 0));
        /**
         * The key of the request to render the history for.
         * Both `request` and `project` properties must be set to render any view.
         */
        get request() { return this.#request_accessor_storage; }
        set request(value) { this.#request_accessor_storage = value; }
        /**
         * Returns true when the element is correctly setup.
         */
        get isSetup() {
            return !!this.project && !!this.request;
        }
        #querying_accessor_storage = (__runInitializers(this, _request_extraInitializers), __runInitializers(this, _querying_initializers, false
        /**
         * A sorted list of history items by the `midnight` property.
         */
        ));
        /**
         * Whether querying for a page of results.
         */
        get querying() { return this.#querying_accessor_storage; }
        set querying(value) { this.#querying_accessor_storage = value; }
        #model_accessor_storage = (__runInitializers(this, _querying_extraInitializers), __runInitializers(this, _model_initializers, void 0));
        /**
         * A sorted list of history items by the `midnight` property.
         */
        get model() { return this.#model_accessor_storage; }
        set model(value) { this.#model_accessor_storage = value; }
        #selectedItem_accessor_storage = (__runInitializers(this, _model_extraInitializers), __runInitializers(this, _selectedItem_initializers, void 0));
        /**
         * The key of the selected request, if any
         */
        get selectedItem() { return this.#selectedItem_accessor_storage; }
        set selectedItem(value) { this.#selectedItem_accessor_storage = value; }
        #renderedItem_accessor_storage = (__runInitializers(this, _selectedItem_extraInitializers), __runInitializers(this, _renderedItem_initializers, void 0));
        /**
         * The currently rendered item.
         */
        get renderedItem() { return this.#renderedItem_accessor_storage; }
        set renderedItem(value) { this.#renderedItem_accessor_storage = value; }
        #view_accessor_storage = (__runInitializers(this, _renderedItem_extraInitializers), __runInitializers(this, _view_initializers, ProjectHistoryView.history));
        get view() { return this.#view_accessor_storage; }
        set view(value) { this.#view_accessor_storage = value; }
        debounceTimeout = __runInitializers(this, _view_extraInitializers);
        /**
         * The cursor returned by the store used in pagination.
         */
        pageCursor;
        willUpdate(cp) {
            if (cp.has('project') || cp.has('request')) {
                this.reset();
                this.queryDebounce();
            }
            if (cp.has('selectedItem')) {
                this.computeSelectedItem();
            }
            super.willUpdate(cp);
        }
        // protected handleBroadcast(e: MessageEvent): void {
        //   const event = e.data as BroadcastEvent
        //   switch (event.operation) {
        //     case 'created':
        //       this.handleHistoryCreated(event as HistoryCreatedBroadcastEvent)
        //       break
        //     case 'deleted':
        //       this.handleHistoryDeleted(event as HistoryDeletedBroadcastEvent)
        //       break
        //     default:
        //   }
        // }
        // protected handleHistoryCreated(event: HistoryCreatedBroadcastEvent): void {
        //   const { request, data } = event
        //   const item = data as IHttpHistory
        //   if (this.request !== request) {
        //     return
        //   }
        //   if (item) {
        //     this.addItem(item, true)
        //   }
        // }
        // protected handleHistoryDeleted(event: HistoryDeletedBroadcastEvent): void {
        //   const { model, history } = this
        //   const { key, request } = event
        //   if (!model || !history || request !== this.request) {
        //     return
        //   }
        //   const historyIndex = history.findIndex((i) => i.key === key)
        //   if (historyIndex < 0) {
        //     return
        //   }
        //   history.splice(historyIndex, 1)
        //   for (const group of model) {
        //     const index = group.findIndex((i) => i.key === key)
        //     if (index >= 0) {
        //       group.splice(index, 1)
        //       if (group.length === 0) {
        //         const groupIndex = model.indexOf(group)
        //         model.splice(groupIndex, 1)
        //       }
        //       this.requestUpdate()
        //       this.setupVisibility()
        //       break
        //     }
        //   }
        // }
        setupVisibility() {
            if (this.history && !this.history.length) {
                this.setAttribute('hidden', '');
            }
            else {
                this.removeAttribute('hidden');
            }
        }
        reset() {
            this.history = [];
            this.pageCursor = undefined;
            this.model = undefined;
            this.selectedItem = undefined;
            this.setupVisibility();
            if (this.debounceTimeout) {
                clearTimeout(this.debounceTimeout);
                this.debounceTimeout = undefined;
            }
        }
        queryDebounce() {
            if (!this.isSetup) {
                return;
            }
            if (this.debounceTimeout) {
                clearTimeout(this.debounceTimeout);
            }
            this.debounceTimeout = setTimeout(() => {
                delete this.debounceTimeout;
                this.queryPage();
            }, 1);
        }
        async queryPage() {
            const { project, request, pageCursor } = this;
            if (!project || !request || this.querying) {
                // todo: set `dirty` flag and re-query after finish.
                return;
            }
            this.querying = true;
            const init = {
                type: 'request',
                project,
                id: request,
                organization: '', // this is filled by the bindings
            };
            if (pageCursor) {
                init.cursor = pageCursor;
            }
            try {
                throw new Error(`Not implemented yet.`);
                // const result = await Events.Store.History.list(init, this)
                // if (!result) {
                //   return
                // }
                // const { items, cursor } = result
                // if (items) {
                //   this.addItems(items)
                // }
                // if (cursor) {
                //   this.pageCursor = cursor
                // }
            }
            finally {
                this.querying = false;
                this.setupVisibility();
            }
        }
        addItems(items) {
            const { history = [] } = this;
            this.history = history.concat(items);
            const all = history.concat(items);
            const sorted = this.sortHistory(all);
            this.model = sorted;
            this.computeSelectedItem();
            if (sorted.length && !this.renderedItem) {
                this.selectedItem = sorted[0][0].key;
            }
            this.setupVisibility();
        }
        /**
         * Adds a new history to the list.
         * This is used instead of re-assigning the entire array and triggering initial computations.
         * @param item The history item to add
         * @param select When true it makes this item selected,
         */
        addItem(item, select) {
            const { model } = this;
            this.history.push(item);
            if (!model || !model.length) {
                this.model = [[item]];
                this.selectedItem = item.key;
                this.requestUpdate();
                this.setupVisibility();
                return;
            }
            const { midnight = Date.now() } = item;
            const index = model.findIndex((i) => i[0].midnight === midnight);
            if (index >= 0) {
                this.pushSorted(model[index], item);
            }
            else {
                const position = model.findIndex((i) => ((i[0] && i[0].midnight) || 0) < midnight);
                model.splice(position, 0, [item]);
            }
            if (select && item.key) {
                this.selectedItem = item.key;
            }
            this.requestUpdate();
            this.setupVisibility();
        }
        sortHistory(history) {
            if (!Array.isArray(history) || !history.length) {
                return [];
            }
            const result = [];
            const now = Date.now();
            history.forEach((current) => {
                const { midnight = now } = current;
                const index = result.findIndex((i) => i[0].midnight === midnight);
                if (index >= 0) {
                    this.pushSorted(result[index], current);
                }
                else if (result[0] && result[0][0] && result[0][0].midnight && result[0][0].midnight < midnight) {
                    result.unshift([current]);
                }
                else {
                    result.push([current]);
                }
            });
            return result;
        }
        computeSelectedItem() {
            const { history, selectedItem } = this;
            this.renderedItem = undefined;
            if (!selectedItem || !Array.isArray(history)) {
                return;
            }
            this.renderedItem = history.find((i) => i.key === selectedItem);
        }
        /**
         * Allows to push a history item into a right index depending on the `created` property
         * so the data processor can skip sorting each group after mutation.
         *
         * The item is inserted before an item that is older (has lower `created` property).
         *
         * @param list The list to insert the item to
         * @param item The item to insert.
         */
        pushSorted(list, item) {
            const older = list.findIndex((i) => i.created < item.created);
            if (older >= 0) {
                list.splice(older, 0, item);
            }
            else {
                // this item is the oldest
                list.push(item);
            }
        }
        handleSelection(e) {
            const item = e.detail.item;
            if (!item) {
                return;
            }
            const { key } = item.dataset;
            if (!key) {
                return;
            }
            this.selectedItem = key;
        }
        handleViewSelection(e) {
            if (!e.detail.selected) {
                return;
            }
            const button = e.detail.button;
            const view = button.dataset.value;
            if (view) {
                this.view = view;
            }
        }
        handleHistoryOption(e) {
            const element = e.detail.item;
            const { option } = element.dataset;
            if (!option) {
                return;
            }
            if (option === 'clear') {
                this.triggerClearHistoryDialog();
            }
        }
        triggerClearHistoryDialog() {
            const dialog = this.shadowRoot?.querySelector('.clear-dialog');
            if (!dialog) {
                return;
            }
            dialog.open = true;
        }
        handleClearHistoryDialogResult(e) {
            if (e.detail.cancelled) {
                return;
            }
            this.clearHistory();
        }
        async clearHistory() {
            const { project, request } = this;
            if (!project || !request) {
                return;
            }
            // space parameter is filled by bindings.
            throw new Error(`Not implemented yet.`);
            // await Events.Store.History.clearRequest('', project, request, this)
            // SnackNotifications.notify('History cleared')
            // this.reset()
        }
        handleItemDelete(e) {
            const button = e.currentTarget;
            const { key } = button.dataset;
            if (!key) {
                return;
            }
            this.deleteHistoryItem(key);
        }
        async deleteHistoryItem(key) {
            if (!key) {
                return;
            }
            const item = this.history.find((i) => i.key === key);
            if (!item) {
                return;
            }
            throw new Error(`Not implemented yet.`);
            // await Events.Store.History.delete(key, this.request, this)
            // SnackNotifications.notify('History item deleted', {
            //   actionLabel: 'Undo',
            //   actionCallback: () => {
            //     this.revertHistoryItemDelete(item)
            //   },
            //   timeout: 7000,
            // })
        }
        async revertHistoryItemDelete(item) {
            throw new Error(`Not implemented yet. ${item}`);
            // const info = SnackNotifications.notify('Reverting...', { persistent: true })
            // try {
            //   await Events.Store.History.create(item, this)
            //   SnackNotifications.cancel(info)
            //   SnackNotifications.notify('Reverted history item.')
            // } catch (e) {
            //   SnackNotifications.cancel(info)
            //   SnackNotifications.notify('Unable to revert changes.')
            // }
        }
        render() {
            const { model } = this;
            if (!model || !model.length) {
                if (this.querying) {
                    return this.renderLoader();
                }
                return nothing;
            }
            return html ` ${this.renderHeader()} ${this.renderView()} ${this.renderClearHistoryDialog()} `;
        }
        renderLoader() {
            return html `<progress></progress>`;
        }
        renderHeader() {
            return html `
      <div class="header">
        ${this.renderHistoryDropdown()} ${this.renderHistoryOptions()} ${this.renderViewToggle()}
      </div>
    `;
        }
        renderHistoryDropdown() {
            const { model } = this;
            const disabled = !model || !model.length;
            return html `
      <ui-dropdown-list
        verticalAlign="top"
        class="history-dropdown"
        closeOnOutsideClick
        @select="${this.handleSelection}"
      >
        <ui-button color="tonal" ?disabled="${disabled}">
          <ui-icon slot="icon" icon="arrowDropDown"></ui-icon>
          History
        </ui-button>
        <ui-list slot="dropdown" role="menu" aria-label="History list"> ${this.renderHistoryList()} </ui-list>
      </ui-dropdown-list>
    `;
        }
        renderHistoryOptions() {
            return html `
      <ui-dropdown-list @select="${this.handleHistoryOption}" closeOnOutsideClick class="dropdown">
        <ui-icon-button aria-label="Additional options"
          ><ui-icon icon="moreVert" role="presentation"></ui-icon
        ></ui-icon-button>
        <ui-list slot="dropdown" role="menu">
          <ui-list-item role="menuitem" data-option="clear" image="icon" title="Deletes all history for the request">
            <ui-icon role="presentation" slot="start" icon="deleteOutline"></ui-icon>
            Clear history
          </ui-list-item>
        </ui-list>
      </ui-dropdown-list>
    `;
        }
        renderHistoryList() {
            const { model } = this;
            if (!Array.isArray(model) || !model.length) {
                return nothing;
            }
            return model.map((group) => this.renderHistoryGroup(group));
        }
        renderHistoryGroup(group) {
            if (!group.length) {
                // this should not happen as the model is computed internally,
                // but I prefer to do just one check to not stop execution when error.
                return nothing;
            }
            const [first] = group;
            return html ` ${this.renderHistoryGroupTitle(first)} ${group.map((item) => this.renderHistoryItem(item))} `;
        }
        renderHistoryGroupTitle(item) {
            const { midnight = 0 } = item;
            return html `
      <div class="history-group-title">${relativeDay(midnight)}</div>
      <ui-divider type="inset"></ui-divider>
    `;
        }
        renderHistoryItem(item) {
            const d = new Date(item.created);
            const isoTime = d.toISOString();
            const { log } = item;
            const { request, response } = log;
            const method = request && request.method;
            const url = request && request.url;
            if (!method || !url) {
                return nothing;
            }
            const error = !response || ErrorResponse.isErrorResponse(response);
            let status = 0;
            let statusText;
            let duration = 0;
            if (!error) {
                const typed = response;
                status = typed.status;
                statusText = typed.statusText;
                duration = typed.loadingTime;
            }
            return html `
      <ui-list-item lines="two" role="menuitem" data-key="${item.key}" data-kind="${item.kind}">
        <div class="history-label">
          <span class="label-value">${statusTemplate(status, statusText)} ${duration} ms</span>
          ${this.renderHistoryItemDelete(item)}
        </div>
        <relative-time slot="supporting-text" datetime="${isoTime}"></relative-time>
      </ui-list-item>
    `;
        }
        renderHistoryItemDelete(item) {
            return html `
      <ui-icon-button
        class="delete"
        aria-label="Delete request"
        data-key="${item.key}"
        data-kind="${item.kind}"
        @click="${this.handleItemDelete}"
        ><ui-icon icon="deleteOutline" role="presentation"></ui-icon
      ></ui-icon-button>
    `;
        }
        renderView() {
            const { view } = this;
            if (view === ProjectHistoryView.analysis) {
                return this.renderAnalysis();
            }
            return this.renderLog();
        }
        renderLog() {
            const { renderedItem } = this;
            if (!renderedItem) {
                return '';
            }
            const typed = renderedItem;
            const data = typed.log ? typed.log : renderedItem;
            return html ` <http-request-log .httpLog="${data}"></http-request-log> `;
        }
        renderAnalysis() {
            const { model } = this;
            if (!model) {
                return '';
            }
            const chart = new HttpHistoryChart(model);
            return chart.durationPlot();
        }
        renderViewToggle() {
            return html `
      <div class="view-toggle">
        <ui-segmented-button-set @select="${this.handleViewSelection}">
          <ui-segmented-button selected title="Shows response history" data-value="history"
            >History</ui-segmented-button
          >
          <ui-segmented-button title="Shows history analysis" data-value="analysis">Analysis</ui-segmented-button>
        </ui-segmented-button-set>
      </div>
    `;
        }
        renderClearHistoryDialog() {
            return html `
      <ui-dialog modal class="clear-dialog" @close="${this.handleClearHistoryDialogResult}">
        <ui-icon slot="icon" icon="deleteOutline"></ui-icon>
        <span slot="title">Delete history?</span>
        <p>This will clear responses history for this request.</p>
        <ui-button color="text" slot="button" value="dismiss">Cancel</ui-button>
        <ui-button color="text" slot="button" value="confirm">Accept</ui-button>
      </ui-dialog>
    `;
        }
    };
})();
/**
 * An element that renders HTTP history for a configured list of requests.
 * This element is just a view and contains no logic related to store communication
 * or user interactions. Use the dispatched events to handle user interactions.
 */
export default HttpProjectRequestHistory;
//# sourceMappingURL=HttpProjectRequestHistory.js.map