/* eslint-disable @typescript-eslint/no-explicit-any */
import { IOConnectCore } from "@interopio/core";
import { IOConnectBrowser } from "@interopio/browser";
import { IOConnectDesktop } from "@interopio/desktop";
import type { AuthOptions as ManagerAuthOptions } from "@interopio/manager-api";

export namespace IOConnectBrowserPlatform {

    export type LibDomains = "system" | "windows" | "appManager" | "layouts" | "workspaces" | "intents" | "channels" | "notifications" | "extension" | "search" | "themes" | "manager" | "prefs" | "ui";

    export interface RemoteStore {
        /**
         * The url of the remote source of application definitions. The remote source needs to follow the [FDC3 AppDirectory standard](https://github.com/finos/FDC3). The applications provided by the remote need to either be IOConnectApplicationConfig or FDC3ApplicationConfig.
         */
        url: string;

        /**
         * The polling interval for fetching from the remote source in milliseconds. If no pollingInterval is provided, the platform will fetch the definitions just once during startup.
         * This also affects the cache refresh interval, if cache is enabled. Minimum accepted value is 5000.
         * @default 0
         */
        pollingInterval?: number;

        /**
        * The request timeout for fetching application definitions from the remote source in milliseconds.
        * @default 30000
        */
        requestTimeout?: number;

        /**
        * Name-value pairs of headers, which will be appended to every request to the provided url.
        */
        customHeaders?: {
            [key: string]: string;
        };

        /**
        * A function which is expected to return a RequestInit object, which will be merged with the default one and appended to every request to the provided url.
        */
        getRequestInit?: () => RequestInit;

        /**
         * If `true`, the platform will wait for the initial response before proceeding with the platform bootstrap.
         * @default true
         */
        waitInitialResponse?: boolean;

        /**
         * If cache is enabled the platform will cache the fetched definitions and respond with cache entries to the API calls.
         */
        cache?: RemoteStoreCacheConfig;
    }

    export interface RemoteStoreCacheConfig {

        /**
         * If `true`, the platform will cache the fetched definitions in the browser's indexDB.
         * @default false
         */
        enabled?: boolean;
    }

    export namespace Applications {
        export interface Config {
            local?: Array<IOConnectBrowser.AppManager.Definition | IOConnectBrowser.AppManager.FDC3DefinitionV1 | IOConnectBrowser.AppManager.FDC3DefinitionV2>;
            remote?: RemoteStore;
        }
    }

    export namespace Layouts {
        export interface Config {
            mode?: "idb" | "session" | "rest" | "manager";
            local?: IOConnectBrowser.Layouts.Layout[];
            rest?: RemoteStore;
        }
    }

    export namespace Channels {
        /**
         * Provide display hints for FDC3 User Channels intended to be visualized and selectable by end users.
         */
        export interface FDC3ChannelDisplayMetadata {
            /**
             *  A user-readable name for this channel, e.g: `"Red"`. */
            name?: string;
            /**
             *  The color that should be associated within this channel when displaying
             *  this channel in a UI, e.g: `#FF0000`. May be any color value supported by
             *  CSS, e.g. name, hex, rgba, etc.. */
            color?: string;

            /**
             *  A URL of an image that can be used to display this channel. */
            glyph?: string;
        }

        export interface FDC3ChannelMeta {
            id: string;
            displayMetadata?: FDC3ChannelDisplayMetadata;
        }

        export interface ChannelMeta {
            color: string;
            /**
             * Provide to enable FDC3 channel support.
             */
            fdc3?: FDC3ChannelMeta;
            [key: string]: any;
        }

        export interface ChannelDefinition {
            name: string;
            meta: ChannelMeta;
            data?: any;
        }

        export interface Config {
            definitions: ChannelDefinition[];
            /**
             * @default single
             */
            mode?: IOConnectBrowser.Channels.Mode;
        }
    }

    export namespace Prefs {
        export interface Store {
            type?: "local" | "manager" | "rest",
            rest?: RemoteStore;
        }

        export interface Config {
            store?: Store;
            validNonExistentApps?: string[];
        }
    }

    /**
     * Configuration for publishing OpenTelemetry data and settings for the published data items.
     */
    export namespace Otel {
        /**
         * Configuration for publishing OpenTelemetry data.
         */
        export interface Config {
            /**
             * Additional attributes to add to the observability resource definition.
             */
            additionalResourceAttributes?: Record<string, unknown>;

            /**
             * A function that returns additional attributes to add to observability entities (metrics, spans, log entries) by default.
             */
            getAdditionalAttributes?: () => Record<string, unknown>;

            /**
             * Additional headers to send in HTTP requests.
             */
            headers?: Record<string, string>;

            /**
             * Settings for publishing OpenTelemetry metrics.
             */
            metrics?: MetricsDefinition;
        }

        /**
         * Settings for publishing OpenTelemetry metrics.
         */
        export interface MetricsDefinition {
            /**
             * URL pointing to an OpenTelemetry metrics server.
             */
            url: string;

            /**
             * Interval in milliseconds at which to publish the generated metrics.
             * @default 30000
             */
            publishInterval?: number;

            /**
             * Definitions of the OpenTelemetry metrics to publish.
             */
            metrics?: MetricDefinition[];

            /**
             * Additional attributes to add to the observability resource definition.
             */
            additionalResourceAttributes?: Record<string, unknown>;

            /**
             * A function that returns additional attributes to add to observability entities (metrics, spans, log entries) by default.
             */
            getAdditionalAttributes?: () => Record<string, unknown>;

            /**
             * Additional headers to send in HTTP requests.
             */
            headers?: Record<string, string>;
        }

        /**
         * Definition of an OpenTelemetry metric.
         */
        export interface MetricDefinition {
            /**
             * If `true`, will enable publishing of the OpenTelemetry metric.
             */
            enabled?: boolean;

            /**
             * Name for the OpenTelemetry metric. May be used in visualization tools.
             */
            name?: string;

            /**
             * Description for the OpenTelemetry metric.
             */
            description?: string;

            /**
             * Specify explicit bucket boundaries for the OpenTelemetry SDK if the metric has been created by a `HistogramAggregator`.
             */
            buckets?: number[];

            /**
             * Type of the predefined OpenTelemetry metric.
             */
            type:
            "app_started" |
            "app_stopped" |
            "app_startup" |
            "app_count" |
            "app_duration" |
            "app_error" |
            "layout_startup" |
            "workspace_startup" |
            "workspace_stopped" |
            "workspace_count" |
            "platform_startup" |
            "platform_error"
        }
    }

    export namespace ServiceWorker {
        export interface Config {
            url?: string;
            registrationPromise?: Promise<ServiceWorkerRegistration>;
        }
    }

    export namespace Plugins {

        export interface InterceptorRegistrationRequest {
            callInterceptor: (config: ControlMessage) => Promise<any>;
            interceptions: Array<{ domain: LibDomains, operation: string }>;
        }

        export interface BaseControlMessage {
            domain: LibDomains;
            operation: string;
            data: any;
            settings?: ControlSettings;
        }

        export interface ControlSettings {
            skipInterception?: boolean;
        }

        export interface PluginInterception {
            register: (request: InterceptorRegistrationRequest) => Promise<void>
        }

        export interface PlatformControls {
            control: (args: BaseControlMessage) => Promise<any>;
            interception: PluginInterception;
            platformApi: API;
            logger?: IOConnectBrowser.Logger.API;
            system: {
                sendControl: (args: BaseControlMessage) => Promise<any>;
            }
        }

        export interface PluginDefinition {
            name: string;
            start: (io: IOConnectBrowser.API, config: any, platform: PlatformControls) => Promise<void> | void;
            version?: string;
            config?: any;
            critical?: boolean;
            stop?: () => void;
        }

        export interface Config {
            definitions: PluginDefinition[];
        }
    }

    export namespace Gateway {

        export type LogLevel = "trace" | "debug" | "info" | "warn" | "error";

        export type LogAppender = (logInfo: LogInfo) => void;

        export interface LogInfo {
            time: Date;
            output: string;
            level: string;
            line: number;
            message: string;
            namespace: string;
            stacktrace: string;
        }

        export interface Config {
            logging?: {
                level?: LogLevel;
                appender?: LogAppender;
            };
            clients?: {
                buffer_size?: number
            }
            bridge?: {
                url: string
            }
        }
    }

    export namespace Connection {

        export interface PreferredConnectionSettings {
            url: string;
            auth?: IOConnectCore.Auth;
            forceIncompleteSwitch?: boolean;
            discoveryIntervalMS?: number;
        }

        export interface Config {
            alwaysPlatform?: boolean;
            preferred?: PreferredConnectionSettings;
            enableManualSwitching?: boolean;
            allowedClientFallbackOrigin?: string;
            blockList?: string[];
        }
    }

    export namespace Workspaces {
        export interface MaximumActiveWorkspacesRule {
            threshold: number;
        }

        export interface IdleWorkspacesRule {
            idleMSThreshold: number;
        }

        export interface HibernationConfig {
            maximumActiveWorkspaces?: MaximumActiveWorkspacesRule;
            idleWorkspaces?: IdleWorkspacesRule;
        }

        export interface LoadingConfig {
            /**
             * Default restore strategy when opening Workspaces.
             */
            defaultStrategy?: "direct" | "delayed" | "lazy";
            delayed?: {
                /**
                 * Valid only in `delayed` mode. Initial period after which to start loading applications in batches. Defaults to 1000.
                 */
                initialOffsetInterval?: number;
                /**
                 * Valid only in `delayed` mode. Interval in minutes at which to load the application batches. Defaults to 5000.
                 */
                interval?: number;
                /**
                 * Valid only in `delayed` mode. Number of applications in a batch to be loaded at each interval. Defaults to 1.
                 */
                batch?: number;
            };
            /**
             * Visual indicator `Zzz` on tabs of apps which are not loaded yet. Useful for developing and testing purposes.
             */
            showDelayedIndicator?: boolean;
        }

        export interface IFrameSandboxConfig {
            flags: string;
        }

        export interface IFramePermissionsPolicyConfig {
            flags: string;
        }

        export interface Config {
            src: string;
            hibernation?: HibernationConfig;
            loadingStrategy?: LoadingConfig;
            isFrame?: boolean;
            initAsEmpty?: boolean;
            frameCache?: boolean;
            iframeSandbox?: IFrameSandboxConfig;
            iframePermissionsPolicy?: IFramePermissionsPolicyConfig;
        }
    }

    export namespace Windows {
        export interface Config {
            windowResponseTimeoutMs?: number;
            defaultWindowOpenBounds?: IOConnectBrowser.Windows.Bounds;
        }
    }

    export namespace Notifications {
        export interface Config extends IOConnectBrowser.Notifications.Configuration {
            clearNotificationOnClick?: boolean;
        }
    }

    export namespace Manager {


        /**
         * Settings for the requests sent to **io.Manager**.
         */
        export interface RequestsConfig {
            /**
             * Interval in milliseconds to wait for a response from **io.Manager**.
             * @default 10000
             */
            timeout?: number;
            /**
             * Interval in milliseconds to wait for a response to the `openSession` request to **io.Manager** before proceeding from cache.
             * @default 10000
             */
            openSessionTimeout?: number;
            /**
             * Interval in milliseconds to wait for a response to the `closeSession` request to **io.Manager**.
             * @default 10000
             */
            closeSessionTimeout?: number;
        }

        /**
         * Settings for persisting data received from **io.Manager**.
         */
        export interface CacheConfig {
            /**
             * If `true`, will enable caching and persisting data from **io.Manager** locally (e.g., in case of connection interruptions).
             * @default true
             */
            enabled?: boolean;
            /**
             * If `true`, on opening a new session to **io.Manager**, all cache databases from previous sessions will be deleted.
             * @default false
             */
            clearOld?: boolean;
        }

        /**
         * Settings for configuring what stores to be pulled from **io.Manager**.
         */
        export interface ManagerFeatures {
            /**
             * If `true`, the library will pull the latest application definitions from **io.Manager**.
             * @default true
             */
            applicationsStore?: boolean;

            /**
             * If `true`, the library will pull the latest layouts definitions from **io.Manager**.
             * @default true
             */
            layoutsStore?: boolean;

            /**
             * If `true`, the library will pull the latest applications preferences from **io.Manager**.
             * @default true
             */
            preferencesStore?: boolean;
        }

        export interface Config {
            /**
             * URL pointing to **io.Manager**.
             */
            url: string;

            /**
             * User authentication configuration.
             */
            auth: ManagerAuthOptions;

            /**
             * Deprecated. If `true`, the library will wait for this module to be fully operational before completing its initialization.
             * @default false
             * @deprecated
             */
            critical?: boolean;

            /**
             * Object containing key/value pairs of headers to be sent with every request.
             */
            headers?: {
                [key: string]: string;
            };

            /**
             * Callback that will be invoked on every request. Use this callback to provide extra headers that will be applied to the request.
             */
            getHeaders?: (req: any) => Promise<Record<string, string>>;

            /**
             * Settings for the requests sent to **io.Manager**.
             */
            requests?: RequestsConfig;

            /**
             * Settings for persisting data received from **io.Manager**.
             */
            cache?: CacheConfig;

            /**
             * Interval in milliseconds at which the server will be polled for new data (apps, Layouts).
             * @default 60000
             */
            fetchIntervalMS?: number;

            /**
             * Interval in milliseconds at which **io.Connect Browser** will try to refresh the **io.Manager** token.
             * @default 3600000
             */
            tokenRefreshIntervalMS?: number;

            /**
             * Settings for configuring what stores to be pulled from **io.Manager**.
             */
            features?: ManagerFeatures

            /**
             * Deprecated. Use `requests.timeout` instead. Interval in milliseconds to wait for a response from **io.Manager**.
             * @default 10000
             * @deprecated
             */
            responseTimeoutMS?: number;
        }
    }

    export namespace Themes {
        export interface Config {
            defaultTheme?: "os" | "light" | "dark"
        }
    }

    export namespace User {
        export interface Config {
            id: string;
            username?: string;
            firstName?: string;
            lastName?: string;
            email?: string;
            type?: string;
            role?: string;
            meta?: any;
        }
    }

    export interface ControlMessage extends Plugins.BaseControlMessage {
        callerType: "plugin" | "client";
        callerId: string;
        commandId: string;
    }

    export namespace Widget {

        export type DefaultConfig = Omit<IOConnectBrowser.WidgetConfig, "enable">;

        export interface Config {
            sources: Sources;
            defaultConfig?: DefaultConfig;
            blockList?: string[];
        }

        export interface Sources {
            bundle: string;
            styles: string[];
            fonts?: string[];
        }
    }

    export namespace Modals {
        export interface Config {
            sources: Sources;
            blockList?: string[];
        }

        export interface Sources {
            bundle: string;
            styles: string[];
            fonts?: string[];
        }
    }

    export namespace IntentResolver {
        export interface Config {
            sources: Sources;
            blockList?: string[];
        }

        export interface Sources {
            bundle: string;
            styles: string[];
            fonts?: string[];
        }
    }

    export interface Config {
        licenseKey: string;
        clientOnly?: boolean;
        windows?: Windows.Config;
        notifications?: Notifications.Config;
        applications?: Applications.Config;
        layouts?: Layouts.Config;
        channels?: Channels.Config;
        plugins?: Plugins.Config;
        serviceWorker?: ServiceWorker.Config;
        gateway?: Gateway.Config;
        connection?: Connection.Config;
        browser?: IOConnectBrowser.Config;
        workspaces?: Workspaces.Config;
        manager?: Manager.Config;
        themes?: Themes.Config;
        user?: User.Config;
        environment?: any;
        applicationPreferences?: Prefs.Config;
        otel?: Otel.Config;
        widget?: Widget.Config;
        modals?: Modals.Config;
        intentResolver?: IntentResolver.Config;
        browserFactory?: (config?: IOConnectBrowser.Config) => Promise<IOConnectBrowser.API>;
    }

    export interface API {
        version: string;
        corePlus?: { version: string };
        system: {
            shutdown(): Promise<void>
        }
    }

    export interface SystemInfo {
        web: {
            version: string;
        };
        platform: {
            version: string;
            plugins: Array<{ name: string, version: string }>;
        };
        workspaces?: {
            version: string;
            frameUrl?: string;
        };
    }
}

export type IOConnectBrowserPlatformFactoryFunction = (config: IOConnectBrowserPlatform.Config) => Promise<{ io: IOConnectBrowser.API | IOConnectDesktop.API; platform?: IOConnectBrowserPlatform.API }>;

declare const IOConnectBrowserPlatformFactory: IOConnectBrowserPlatformFactoryFunction;

export default IOConnectBrowserPlatformFactory;
