import {GatewayServer} from '../../../gateway-server';
import {IOGateway} from '@interopio/gateway';
import { GatewayConfig, MetricsPublisherConfig } from '../../../types/gateway-ent';

function toMetricsFilters(legacy?: MetricsPublisherConfig['filters']): IOGateway.MetricFilters | undefined {
    if (legacy) {
        const publishers = legacy.publishers.map(publisher => {
            return {identity: publisher.publisher, metrics: publisher.metrics};
        });
        const non_matched = legacy['non-matched'];
        return {publishers, non_matched};
    }
}

function toMetricsPublisherConfig(legacy: MetricsPublisherConfig & {
    conflation?: { 'max-datapoints-repo'?: number } & MetricsPublisherConfig['conflation']
}): IOGateway.BasicMetricsPublisherConfig {
    const filters = toMetricsFilters(legacy?.filters);
    const conflation = toConflation(legacy?.conflation);
    return {...legacy, filters, conflation};
}

function toConflation(legacy?: {
    'max-datapoints-repo'?: number
} & MetricsPublisherConfig['conflation']): IOGateway.BasicMetricsPublisherConfig['conflation'] | undefined {
    if (legacy) {
        return {interval: legacy.interval};
    }
}

function toMetricsConfig(legacy: GatewayConfig['metrics']): IOGateway.GatewayConfig['metrics'] {
    const metrics: IOGateway.GatewayConfig['metrics'] = {publishers: []};
    legacy?.publishers?.forEach((publisher) => {
        if (typeof publisher === 'string') {
            if (publisher === 'rest') {
                metrics.publishers.push('rest');
                if (legacy.rest) {
                    const conf = {...legacy.rest};
                    const userAgent = conf["user-agent"];
                    delete conf['user-agent'];
                    const headers = {...conf.headers, ...(userAgent ? {'user-agent': userAgent} : {})};
                    delete conf.headers;
                    metrics.rest = {
                        endpoint: conf.endpoint,
                        headers: headers,
                        ...toMetricsPublisherConfig(conf)
                    };
                    metrics.rest['publishFn'] ??= '@interopio/gateway-server/metrics-rest'
                }
            } else if (publisher === 'file') {
                metrics.publishers.push('file');
                if (legacy.file) {
                    const conf = {...legacy.file};
                    const status = conf['skip-status'] === undefined ? true : !conf['skip-status'];
                    delete conf['skip-status'];
                    metrics.file = {
                        location: conf.location,
                        status: status,
                        ...toMetricsPublisherConfig(conf)
                    };
                }
            } else {
                // unsupported predefined type
            }
        } else {
            const configuration = {...publisher.configuration};
            const splitSize = configuration["split-size"];
            delete configuration["split-size"];
            const file = publisher['file'] as string;
            const custom: IOGateway.CustomMetricsPublisherConfig = {
                split_size: splitSize,
                publisher: {file, configuration},
                ...toMetricsPublisherConfig(configuration)
            }
            metrics.publishers.push(custom);
        }
    });
    if (legacy?.filters) {
        metrics.filters = toMetricsFilters(legacy?.filters);
    }
    return metrics;
}
type GatewayCluster = Required<GatewayConfig>['cluster'];
type P2P = Required<GatewayCluster>['p2p'];

function toCluster(legacy?: P2P): IOGateway.MeshConfig['cluster'] | undefined {
    if (legacy?.directory) {
        const legacyDirectory = legacy.directory;
        const config: IOGateway.MeshConfig['cluster'] = {endpoint: legacy?.['endpoint']};
        if (legacyDirectory.type === 'rest') {
            let directory: IOGateway.RestMeshDirectoryConfig | undefined = undefined;
            if (config.endpoint === undefined) {
                config.endpoint = legacyDirectory.config!.directory_uri!;
            }
            else {
                directory = {uri: legacyDirectory.config!.directory_uri};
            }
            if (legacyDirectory.config?.announce_interval) {
                directory ??= {};
                directory.interval = Number(legacyDirectory.config.announce_interval);
            }
            if (directory !== undefined) {
                config.directory = directory;
            }
            return config;
        }
        else if (legacyDirectory.type === 'static') {
            config.directory = {members : legacyDirectory.members ?? []};
            return config;
        }
    }
}

function toMeshConfig(legacy: GatewayConfig['cluster']) {
    const mesh: IOGateway.MeshConfig = {};
    if (legacy?.configuration?.node_id) mesh.node = legacy.configuration.node_id;
    if (legacy?.type === 'broker') mesh.broker = {endpoint: legacy.broker!.endpoint!};
    if (legacy?.type === 'p2p') {
        const cluster = toCluster(legacy.p2p);
        if (cluster) {
            mesh.cluster = cluster;
        }
    }
    return mesh;
}

export function toServerConfig(config: GatewayConfig): GatewayServer.ServerConfig {
    const gateway: GatewayServer.ServerConfig["gateway"] = {
        route: config.route,
        limits: config.limits,
        origins: config.security?.origin_filters as GatewayServer.OriginFilters,
        clients: config['clients'] ?? {inactive_seconds: 0, buffer_size: 100},
        contexts: {
            lifetime: 'retained',
            visibility: [
                {context: /___channel___.+/, restrictions: 'cluster'},
                {context: /T42\..+/, restrictions: 'local'}
            ]
        },
        methods: {
            visibility: [
                {method: /T42\..+/, restrictions: 'local'}
            ]
        },
        peers: {
            visibility: [
                {domain: 'context', restrictions: 'cluster'},
                {domain: 'agm', restrictions: 'local'}
            ]
        },
        metrics: {publishers: []}
    };
    if (config.authentication?.token_ttl) gateway.token = {ttl: config.authentication?.token_ttl as number};
    if (config['contexts']) {
        gateway.contexts = config['contexts'];
    }
    if (config['methods']) {
        gateway.methods = config['methods'];
    }
    if (config['peers']) {
        gateway.peers = config['peers'];
    }
    if (config.cluster?.enabled) {
        gateway.mesh = toMeshConfig(config.cluster);

    }
    if (config.metrics?.publishers) gateway.metrics = toMetricsConfig(config.metrics);
    return {
        port: config.port ?? 3434,
        host: config.ip ?? config['host'],
        memory: config['memory'],
        gateway: gateway
    };
}
