import { Singleton, Container, Scope, Inject } from "typescript-ioc";
import { DeploymentParam, DeployPost, PVCParam, ServiceParam, ServicePost } from '../refs/k8s-api'
import { LoggerBuilder } from './LoggerBuilder'
import { SimpleConfigClient } from './SimpleConfigClient'
import { DaoUtils } from '../refs/cm-tools'
import { SysBasicCache } from './SysBasicCache'
import { K8SClientFactory } from './K8SClientFactory'
import { AppOptionsConfig, AppOptionsConfigChcker, DeployConfig } from '../refs/cm-models';
import { logger, OpResult, Value } from "mslib"

@Singleton
export class K8SDeployParamBuilder {

    @Inject
    private configClient: SimpleConfigClient
    @Inject
    private sysBasicCache: SysBasicCache
    @Inject
    private k8sClientFactory: K8SClientFactory
    @Value('kt-me-hosts')
    private ktMeHosts: any


    /**
     * 构建K8S部署参数
     * @param deployConfig 基础服务部署配置信息
     * @param logloggerBuilder 日志构建器，用于管理后台显示部署日志详情
     */
    async build(deployConfig: DeployConfig, loggerBuilder: LoggerBuilder): Promise<OpResult<any>> {
       
        let op = this.acquireAppOptionsConfig(deployConfig, loggerBuilder)
        if (!op.isSuccess()) {
            return op;
        }
        loggerBuilder.appendln(`解析应用选项配置完成......`)
        const appOptionsConfig: AppOptionsConfig = op.data
        const k8sClient = this.k8sClientFactory.getClient(deployConfig.k8sId)

        op = await this.acquireEnvs(deployConfig, appOptionsConfig, k8sClient.config.cloudProvider, loggerBuilder)
        if (!op.isSuccess()) {
            return op;
        }
        loggerBuilder.appendln('解析配置环境变量完成......')
        const envs = op.data
        const k8sConfig = k8sClient.config
        const appName = this.buildAppName(deployConfig.appType, deployConfig.id)

        const data: BuildResult = {
            deployParams: []
        }

        for (let i = 0; i < appOptionsConfig.elasticConfig.minInstance; i++) {
            const uuid = DaoUtils.uuid()
            const podName = `${appName}-${uuid.substring(0, 10)}-${i}`
            const deployParam: DeploymentParam = {
                deployId: deployConfig.id, //部署ID 作为selector
                appName: podName,
                cpu: appOptionsConfig.specConfig.instanceCpu,
                memory: appOptionsConfig.specConfig.instanceMemory,
                image: deployConfig.image,
                command: deployConfig.startScript,
                replicas: 1,
                labels: {
                    appType: deployConfig.appType,
                    deployId: deployConfig.id,
                    deployTag: `${deployConfig.deployTag}`
                },
                envs,
                imagePullSecret: k8sConfig.defaultImagePullSecret,
                pvc: {
                    name: podName,
                    mountPath: '/ext'
                },
                ports: this.parsePorts(deployConfig.imagePortMapping),
            }

            const podDeployParam: PodDeployParam = { deployParam }
            if (appOptionsConfig.pvcConfig && appOptionsConfig.pvcConfig.enabled === true) {
                const pvcParam: PVCParam = {
                    name: podName,
                    storage: (appOptionsConfig.pvcConfig.diskSize || (k8sConfig.defaultStorage || 20)) + '',
                    storageClassName: k8sConfig.defaultStorageClassName
                }
                podDeployParam.pvcParam = pvcParam
            }
            data.deployParams.push(podDeployParam)
        }

        if (deployConfig.slbPortMapping) {
            const ports = this.parseServicePorts(deployConfig.slbPortMapping)
            if (ports.length > 0) {
                const param: ServiceParam = {
                    name: appName,
                    podLabel: deployConfig.id, //部署ID 作为selector
                    networkType: deployConfig.networkType, //网络类型
                    slbId: deployConfig.slbId,
                    ports: ports
                }
                data.serviceParam = param
            }
        }
        loggerBuilder.appendln('参数构建完成，准备开始部署K8S---')

        return OpResult.success('OK', data)
    }

    buildAppName(appType: string, deployId: string): string {
        return `${appType}-${deployId}`.toLowerCase()
    }

    /**
     * 解析获取应用选项配置信息
     */
    private acquireAppOptionsConfig(deployConfig: DeployConfig, loggerBuilder: LoggerBuilder): OpResult<any> {
        if (!deployConfig.appOptionsConfig) {
            loggerBuilder.appendln(`部署失败，应用选项配置JSON为空[appOptionsConfig]`)
            return OpResult.error(loggerBuilder.toString())
        }

        const appOptionsConfig: AppOptionsConfig = deployConfig.appOptionsConfig
        let op = AppOptionsConfigChcker.check(appOptionsConfig)
        if (!op.isSuccess()) {
            loggerBuilder.appendln(`部署失败，${op.message}`)
            return OpResult.error(loggerBuilder.toString())
        }
        return OpResult.success('OK', appOptionsConfig)
    }

    /**
     * 获取环境变量信息
     */
    private async acquireEnvs(deployConfig: DeployConfig, appOptionsConfig: AppOptionsConfig, cloudProvider: string, loggerBuilder: LoggerBuilder): Promise<OpResult<any>> {
        //1. 从配置中心中获取
        let op = await this.acquireConfigs(deployConfig, loggerBuilder)
        if (!op.isSuccess()) {
            return op;
        }

        //2. 从部署配置中合并
        let envs = Object.assign(deployConfig.envs || {}, op.data);

        //3. 中台固有环境变量设置
        envs['APP_TYPE'] = deployConfig.appType
        envs['KT_DEPLOY_ID'] = deployConfig.id
        envs['KT_PROJECT_NAME'] = deployConfig.name
        envs['KT_APP_NAME'] = deployConfig.name
        envs['KT_LOG_FILE_NAME'] = 'output.log'

        envs['KT_LOG_PATH'] = '/ext/logs'
        envs['KT_K8S_ID'] = deployConfig.k8sId
        envs['KT_K8S_DEPLOYMENT'] = deployConfig.id
        envs['KT_CONFIG_SERVER_URL'] = this.getConfigServerUrl(deployConfig)

        //日志kafka，非阿里云K8S环境走外网IP
        const logConfig = appOptionsConfig.logConfig;
        const logKafkaServer = logConfig.server
        const logKafkaTopic = logConfig.topic
        const logKafkaHost = cloudProvider !== 'ALIYUN' ? logConfig.internetIp : logConfig.ip
        const logKafkaPort = cloudProvider !== 'ALIYUN' ? logConfig.internetPort : logConfig.port

        envs['KT_KAFKA_TOPIC'] = logKafkaTopic
        envs['KT_KAFKA_SERVER'] = logKafkaServer
        envs['KT_KAFKA_PORT'] = logKafkaPort


        let hosts = []
        //业务系统配置在配置中心的KT_ME_HOSTS参数
        if (envs['KT_ME_HOSTS'] && envs['KT_ME_HOSTS'].length > 0) {
            hosts = hosts.concat(envs['KT_ME_HOSTS'].toString().split('|'))
        }
        //中台服务的HOST设置
        if (this.ktMeHosts && this.ktMeHosts.length > 0) {
            hosts = hosts.concat(this.ktMeHosts)
        }
        //kafka host
        hosts.push(`${logKafkaServer}:${logKafkaHost}`)


        if (hosts.length > 0) {
            envs['KT_ME_HOSTS'] = hosts.join('|')
        }

        for (let key in envs) {
            if (!envs[key] && envs[key] != 0 && envs[key] != false) {
                delete envs[key];
                continue
            }
            envs[key] = envs[key].toString()
        }
        return OpResult.success('OK', envs)
    }


    /**
     * 从配置中心获取配置
     * @param deployConfig 部署配置
     * @param 
     */
    private async acquireConfigs(deployConfig: DeployConfig, loggerBuilder: LoggerBuilder): Promise<OpResult<any>> {
        if (DaoUtils.isEmpty(deployConfig.configCenterCode)) {
            return OpResult.success('OK', {})
        }
        const serviceCenter = this.sysBasicCache.getServiceCenter(deployConfig.serviceCenterId)
        const sysEnv = this.sysBasicCache.getSysEnv(deployConfig.envId)
        const configCodes = deployConfig.configCenterCode

        let configObj = {}
        for (let config of configCodes) {

            const configCode = `${serviceCenter.code}.${config.code}`
            let configVersion = config.version
            loggerBuilder.appendln(`准备加载配置文件${configCode}/${configVersion}......`)
            let configServerUrl = this.getConfigServerUrl(deployConfig) + '/getConfigFile/json'

            const op = await this.configClient.getConfig(configServerUrl, deployConfig.envCode, sysEnv.envKey, configCode, configVersion)
            if (op.isSuccess()) {
                loggerBuilder.appendln(`加载配置文件成功${configCode}/${configVersion}`)
                configObj = Object.assign(configObj, op.data);
            } else {
                loggerBuilder.appendln(`加载配置文件失败${configCode}/${configVersion},message:${op.message}`)
                return op
            }
        }
        return OpResult.success('OK', configObj)

    }

    private getConfigServerUrl(deployConfig: DeployConfig): string {
        let configServerUrl = process.env['KT_CONFIG_SERVER_URL']
        if (deployConfig.serviceCenterId === 'b049c1c4fd1c4baa8982521de3a3de16') { //开发环境服务中心
            configServerUrl = 'http://47.114.9.86:3008/conf'
        }
        return configServerUrl
    }


    /**
     * 端口号配置解析
     * @param 
     */
    private parseServicePorts(ports: Array<any>): Array<ServicePost> {
        if (!ports) {
            return []
        }
        const result = []
        for (let portConfig of ports) {
            if (portConfig.targetPort && portConfig.slbPort) {
                const obj: ServicePost = {
                    targetPort: parseInt(portConfig.targetPort.toString()),
                    protocol: portConfig.protocol,
                    nodePort: parseInt(portConfig.slbPort.toString()),
                }
                result.push(obj)
            }
        }
        return result
    }
    /**
     * 端口号配置解析
     * @param 
     */
    private parsePorts(ports: Array<any>): Array<DeployPost> {
        if (!ports) {
            return []
        }
        const result = []
        for (let portConfig of ports) {
            if (portConfig.port.indexOf('-') > -1) {
                const range = portConfig.port.split('-')
                if (range.length == 2) {
                    let portRange = this.getRangePorts(range[0], range[1])
                    for (let p of portRange) {
                        const obj: DeployPost = {
                            port: p,
                            protocol: portConfig.protocol
                        }
                        result.push(obj)
                    }
                }
            } else {
                const obj: DeployPost = {
                    port: portConfig.port,
                    protocol: portConfig.protocol
                }
                result.push(obj)
            }
        }
        return result
    }


    private getRangePorts(begin: string, end: string): Array<number> {
        const result = []
        const _begin = parseInt(begin)
        const _end = parseInt(end)
        for (let i = _begin; i <= _end; i++) {
            result.push(i)
        }
        return result
    }


}

export interface BuildResult {
    deployParams?: Array<PodDeployParam>,
    serviceParam?: ServiceParam
}

export interface PodDeployParam {
    pvcParam?: PVCParam,
    deployParam?: DeploymentParam,
}