var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
/*
 * Copyright 2025, OpenRemote Inc.
 *
 * See the CONTRIBUTORS.txt file in the distribution for a
 * full listing of individual contributors.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
import { css, html } from "lit";
import { customElement, state } from "lit/decorators.js";
import { Page, router } from "@openremote/or-app";
import { createSelector } from "@reduxjs/toolkit";
import { manager } from "@openremote/core";
import "@openremote/or-services";
import { getServicesRoute } from "../routes";
import { showSnackbar } from "@openremote/or-mwc-components/or-mwc-snackbar";
import { i18next } from "@openremote/or-translate";
export function pageServicesProvider(store) {
    return {
        name: "services",
        routes: ["services", "services/:serviceName"],
        pageCreator: () => {
            const page = new PageServices(store);
            return page;
        },
    };
}
const serviceStyles = css `
    :host {
        overflow: hidden;
    }

    #services {
        z-index: 0;
        background: transparent;
    }
`;
/**
 * Consolidate services by serviceId, preferring AVAILABLE over UNAVAILABLE
 * @param services - Array of services to consolidate
 * @returns Consolidated array with unique serviceIds
 */
function consolidateServices(services) {
    return Object.values(services.reduce((acc, service) => {
        const serviceId = service.serviceId || "";
        const existing = acc[serviceId];
        if (!existing ||
            (service.status === "AVAILABLE" /* ExternalServiceStatus.AVAILABLE */ && existing.status !== "AVAILABLE" /* ExternalServiceStatus.AVAILABLE */)) {
            acc[serviceId] = service;
        }
        return acc;
    }, {}));
}
let PageServices = class PageServices extends Page {
    static get styles() {
        return [serviceStyles];
    }
    get name() {
        return "services";
    }
    constructor(store) {
        super(store);
        this.serviceId = null;
        this.realmName = "";
        this.services = [];
        this.selectedService = null;
        this._loading = false;
        this.realmSelector = (state) => state.app.realm || manager.config.realm;
        this.getRealmState = createSelector([this.realmSelector], (realm) => {
            this.realmName = realm;
        });
        this.realmName = manager.displayRealm;
    }
    connectedCallback() {
        super.connectedCallback();
        this._subscribeEvents();
    }
    disconnectedCallback() {
        super.disconnectedCallback();
        this._unsubscribeEvents();
    }
    _subscribeEvents() {
        return __awaiter(this, void 0, void 0, function* () {
            if (manager.events) {
                this._eventSubscriptionId = yield manager.events.subscribe({
                    eventType: "external-service",
                }, (ev) => this._onEvent(ev));
            }
        });
    }
    _unsubscribeEvents() {
        var _a;
        if (this._eventSubscriptionId) {
            (_a = manager.events) === null || _a === void 0 ? void 0 : _a.unsubscribe(this._eventSubscriptionId);
        }
    }
    _onEvent(event) {
        if (!event.service || !event.cause) {
            return;
        }
        // Super users receive all events, so we need to filter them out if they are not for the display realm or global
        if (manager.isSuperUser && event.service.realm !== this.realmName && !event.service.isGlobal) {
            return;
        }
        // Handle the event based on the cause
        switch (event.cause) {
            case "REGISTER" /* ExternalServiceEventCause.REGISTER */: {
                this._onServiceRegistered(event);
                break;
            }
            case "UPDATE" /* ExternalServiceEventCause.UPDATE */: {
                this._onServiceUpdated(event);
                break;
            }
            case "DEREGISTER" /* ExternalServiceEventCause.DEREGISTER */: {
                this._onServiceDeregistered(event);
                break;
            }
            default:
                break;
        }
        this.services = consolidateServices(this.services);
    }
    _onServiceRegistered(event) {
        var _a;
        this.services.push(event.service);
        // If the pushed service is the same as the selected service.serviceId and the current selected service is unvailable, refresh the iframe
        if (this._eventIsForSelectedServiceId(event) &&
            ((_a = this.selectedService) === null || _a === void 0 ? void 0 : _a.status) === "UNAVAILABLE" /* ExternalServiceStatus.UNAVAILABLE */) {
            this.selectedService = event.service;
            this._refreshIframe();
        }
    }
    _onServiceUpdated(event) {
        var _a, _b, _c;
        const existingServiceInstance = this.services.find((service) => {
            var _a, _b;
            return service.serviceId === ((_a = event.service) === null || _a === void 0 ? void 0 : _a.serviceId) &&
                service.instanceId === ((_b = event.service) === null || _b === void 0 ? void 0 : _b.instanceId);
        });
        // Overwrite the existing service with the updated service object
        if (existingServiceInstance) {
            Object.assign(existingServiceInstance, event.service);
        }
        // If the selected service instance is the one that was updated, and the status has changed, refresh the iframe
        if (this._eventIsForSelectedServiceInstance(event) &&
            ((_a = this.selectedService) === null || _a === void 0 ? void 0 : _a.status) !== ((_b = event.service) === null || _b === void 0 ? void 0 : _b.status)) {
            this.selectedService = event.service;
            this._refreshIframe();
            return;
        }
        // If the selected serviceId is the same as the event serviceId and the current selected status is unavailable, refresh the iframe
        if (this._eventIsForSelectedServiceId(event) &&
            ((_c = this.selectedService) === null || _c === void 0 ? void 0 : _c.status) === "UNAVAILABLE" /* ExternalServiceStatus.UNAVAILABLE */) {
            this.selectedService = event.service;
            this._refreshIframe();
        }
    }
    _onServiceDeregistered(event) {
        // Filter out the service instance that was deregistered
        this.services = this.services.filter((service) => {
            var _a, _b;
            return service.serviceId !== ((_a = event.service) === null || _a === void 0 ? void 0 : _a.serviceId) &&
                service.instanceId !== ((_b = event.service) === null || _b === void 0 ? void 0 : _b.instanceId);
        });
        // If the selected service instance is the one that was deregistered, set the selected service to null
        if (this._eventIsForSelectedServiceInstance(event)) {
            this.selectedService = null;
        }
    }
    _eventIsForSelectedServiceInstance(event) {
        var _a, _b, _c, _d;
        return (((_a = this.selectedService) === null || _a === void 0 ? void 0 : _a.serviceId) === ((_b = event.service) === null || _b === void 0 ? void 0 : _b.serviceId) &&
            ((_c = this.selectedService) === null || _c === void 0 ? void 0 : _c.instanceId) === ((_d = event.service) === null || _d === void 0 ? void 0 : _d.instanceId));
    }
    _eventIsForSelectedServiceId(event) {
        var _a, _b;
        return ((_a = this.selectedService) === null || _a === void 0 ? void 0 : _a.serviceId) === ((_b = event.service) === null || _b === void 0 ? void 0 : _b.serviceId);
    }
    _loadData() {
        return __awaiter(this, arguments, void 0, function* (silent = false) {
            if (!silent) {
                this._loading = true;
            }
            try {
                // Use promise.allSettled to run both requests in parallel and wait for both to complete
                // If we use Promise.all, we would not be able to handle the case where one request fails and the other succeeds
                const [realmServicesResult, globalServicesResult] = yield Promise.allSettled([
                    manager.rest.api.ExternalServiceResource.getServices({
                        realm: this.realmName,
                    }),
                    manager.rest.api.ExternalServiceResource.getGlobalServices(),
                ]);
                // Temporary array to store all retrieved services
                const retrievedServices = [];
                const realmServicesRequestHasSucceeded = realmServicesResult.status === "fulfilled" && realmServicesResult.value.status === 200;
                // Handle realm services result
                if (realmServicesRequestHasSucceeded) {
                    retrievedServices.push(...realmServicesResult.value.data);
                }
                else {
                    console.error("Failed to load realm services:", realmServicesResult.status);
                    showSnackbar(undefined, i18next.t("service.realmServicesLoadError"));
                }
                // Handle global services result
                const globalServicesRequestHasSucceeded = globalServicesResult.status === "fulfilled" && globalServicesResult.value.status === 200;
                if (globalServicesRequestHasSucceeded) {
                    retrievedServices.push(...globalServicesResult.value.data);
                }
                else {
                    console.error("Failed to load global services:", globalServicesResult.status);
                    showSnackbar(undefined, i18next.t("service.globalServicesLoadError"));
                }
                // Update the services state if we able to retrieve any
                if (retrievedServices.length > 0) {
                    this.services = consolidateServices(retrievedServices);
                    this.requestUpdate();
                    // Re-select the service if serviceId is set and no service is selected
                    if (this.serviceId && !this.selectedService) {
                        const service = this.services.find((service) => service.serviceId === this.serviceId);
                        if (service) {
                            this.selectedService = service;
                        }
                    }
                }
            }
            catch (error) {
                console.error("Unexpected error loading services:", error);
                showSnackbar(undefined, i18next.t("service.servicesLoadError"));
            }
            finally {
                this._loading = false;
            }
        });
    }
    // Reload the current service iframe
    _refreshIframe() {
        var _a;
        const servicesComponent = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector("or-services");
        if (servicesComponent && typeof servicesComponent.refreshIframe === "function") {
            servicesComponent.refreshIframe();
        }
        else {
            console.warn("Unable to reload iframe: services component not found or refreshIframe method not available");
        }
    }
    stateChanged(state) {
        var _a;
        this.getRealmState(state);
        // Update serviceId from URL params - this will trigger updated() which handles the selection
        const newServiceId = (_a = state.app.params) === null || _a === void 0 ? void 0 : _a.serviceName;
        if (newServiceId !== this.serviceId) {
            this.serviceId = newServiceId;
        }
    }
    _onServiceSelected(e) {
        const service = e.detail;
        if (service && service.serviceId !== this.serviceId) {
            this.serviceId = service.serviceId;
            this.selectedService = service;
            this._updateRoute();
        }
    }
    willUpdate(changedProperties) {
        super.willUpdate(changedProperties);
        if (changedProperties.has("serviceId")) {
            if (this.serviceId) {
                this._updateServiceSelection();
            }
        }
        if (changedProperties.has("realmName")) {
            this._loadData();
        }
    }
    _updateRoute() {
        router.navigate(getServicesRoute(this.serviceId));
    }
    _updateServiceSelection() {
        if (this.serviceId) {
            const service = this.services.find((service) => service.serviceId === this.serviceId);
            if (service) {
                this.selectedService = service;
            }
        }
        else {
            this.selectedService = null;
        }
    }
    render() {
        return html `
            <div style="width: 100%;">
                <or-services
                    id="services"
                    .realmName="${this.realmName}"
                    .services="${this.services}"
                    .selectedService="${this.selectedService}"
                    .loading="${this._loading}"
                    @or-service-selected="${this._onServiceSelected}"
                ></or-services>
            </div>
        `;
    }
};
__decorate([
    state()
], PageServices.prototype, "serviceId", void 0);
__decorate([
    state()
], PageServices.prototype, "realmName", void 0);
__decorate([
    state()
], PageServices.prototype, "services", void 0);
__decorate([
    state()
], PageServices.prototype, "selectedService", void 0);
__decorate([
    state()
], PageServices.prototype, "_loading", void 0);
PageServices = __decorate([
    customElement("page-services")
], PageServices);
export { PageServices };
//# sourceMappingURL=page-services.js.map