/* eslint-disable @typescript-eslint/no-explicit-any */
import { IOConnectCore } from "@interopio/core";
import { IOConnectBrowser } from "@interopio/browser";
import { IOConnectDesktop } from "@interopio/desktop";

export namespace IOConnectBrowserPlatform {

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

    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. If no pollingInterval is provided, the platform will fetch the definitions just once during startup.
         */
        pollingInterval?: number;

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

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

    export interface Supplier<T> {
        fetch(): Promise<T>;
        save?(elements: T): Promise<void>;
        delete?(elements: T): Promise<void>;
        timeout?: number;
        pollingInterval?: number;
    }

    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";
            local?: IOConnectBrowser.Layouts.Layout[];
        }
    }

    export namespace Channels {
        export interface ChannelMeta {
            color: string;
            [key: string]: any;
        }

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

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

    export namespace Prefs {
        export interface Store {
            type?: "local" | "manager"
        }

        export interface Config {
            store?: Store;
        }
    }

    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
            }
        }
    }

    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;
        }
    }

    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 Config {
            src: string;
            hibernation?: HibernationConfig;
            loadingStrategy?: LoadingConfig;
            isFrame?: boolean;
            initAsEmpty?: boolean;
            frameCache?: boolean;
            iframeSandbox?: IFrameSandboxConfig
        }
    }

    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 {
        export interface AuthConfig {
            basic?: {
                username: string;
                password: string;
            };
            username?: string;
            token?: {
                bearer?: string;
            };
            includeCredentials?: boolean;
        }

        export interface Config {
            url: string;
            auth: AuthConfig;
            critical?: boolean;
            headers?: {
                [key: string]: string;
            };
            fetchIntervalMS?: number;
            tokenRefreshIntervalMS?: number;
            responseTimeoutMS?: number;
        }
    }

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

    export namespace User {
        export interface Config {
            username: string;
        }
    }

    export interface ControlMessage extends Plugins.BaseControlMessage {
        callerType: "plugin" | "client";
        callerId: string;
        commandId: 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;
        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;
