import { Singleton, Container, Scope, Inject } from "typescript-ioc";
import { logger, OpResult } from "mslib"
import { DeployConfig, CloseDeployConfig } from '../refs/cm-models';
import { LoggerBuilder } from './LoggerBuilder'
import { K8SDeployParamBuilder, BuildResult } from './K8SDeployParamBuilder'
import { K8SClientFactory } from './K8SClientFactory'
import { DeploymentParam, PVCParam, ServiceParam } from '../refs/k8s-api'

/**
 * K8S服务部署
 * @date 2021-06-01 10:34:32
 * @author 吴锋霖
 */
class K8SDeployService {

    @Inject
    private k8sDeployParamBuilder: K8SDeployParamBuilder
    @Inject
    private k8sClientFactory: K8SClientFactory

    /**
     * 服务部署
     * @param deployConfig 部署配置
     * @param loggerBuilder 日志构建器
     */
    async deploy(deployConfig: DeployConfig, loggerBuilder: LoggerBuilder): Promise<OpResult<any>> {

        loggerBuilder.appendln(`准备开始构建部署参数......${deployConfig.id}/${deployConfig.name}`)
        let op = await this.k8sDeployParamBuilder.build(deployConfig, loggerBuilder)
        if (!op.isSuccess()) {
            return op
        }
        const namespace = deployConfig.envCode.toLowerCase()
        const buildResult: BuildResult = op.data;
        const k8sClient = this.k8sClientFactory.getClient(deployConfig.k8sId)

        loggerBuilder.appendln(`参数构建完成，准备开始部署K8S---[${deployConfig.name}]至[${k8sClient.name}/${deployConfig.envCode.toLowerCase()}]`)

        for (let param of buildResult.deployParams) {
            const deployParam: DeploymentParam = param.deployParam
            const pvcParam: PVCParam = param.pvcParam
            if (pvcParam) {
                op = await k8sClient.createPodAndPvc(namespace, deployParam, pvcParam)
            } else {
                op = await k8sClient.createPod(namespace, deployParam)
            }
            if (!op.isSuccess()) {
                loggerBuilder.appendln(`部署POD失败: ${op.message} ......`)
                return op
            }
        }

        const serviceParam: ServiceParam = buildResult.serviceParam
        op = await k8sClient.createService(namespace, serviceParam, true)
        if (!op.isSuccess()) {
            loggerBuilder.appendln(`部署Service失败: ${op.message} ......`)
            return op
        }

        await this.closeOldDeployment(deployConfig, loggerBuilder)

        loggerBuilder.appendln(`部署完成......`)
        return op
    }

    /**
    * 关闭服务部署
    * @param deployConfig 部署配置
    * @param loggerBuilder 日志构建器
    */
    async close(deployConfig: CloseDeployConfig, loggerBuilder: LoggerBuilder): Promise<OpResult<any>> {

        loggerBuilder.appendln('准备关闭部署.....' + deployConfig.id)
        const namespace = deployConfig.envCode.toLowerCase()
        const k8sClient = this.k8sClientFactory.getClient(deployConfig.k8sId)
        let op = await k8sClient.getPodList(namespace)
        if (!op.isSuccess()) {
            loggerBuilder.appendln('获取POD列表异常:' + op.message)
            return;
        }
        //1. 删除POD & PVC
        const podList = op.data
        for (let pod of podList.items) {
            const name = pod.metadata.name
            const labels = pod.metadata.labels
            if (labels.deployId === deployConfig.id) {
                op = await this.closePod(k8sClient, namespace, name, loggerBuilder)
                if (!op.isSuccess()) {
                    return op
                }
            }
        }

        //2. 删除Service
        op = await k8sClient.getServiceList(namespace)
        if (!op.isSuccess()) {
            loggerBuilder.appendln('获取Service列表异常:' + op.message)
            return;
        }
        for (let service of op.data.items) {
            if (service.spec.selector.app === deployConfig.id) {
                op = await k8sClient.deleteService(namespace, service.metadata.name)
                if (!op.isSuccess()) {
                    return op
                }
            }
        }
        return OpResult.success('OK')
    }


    /**
     * 关闭其他旧的部署
     */
    private async closeOldDeployment(deployConfig: DeployConfig, loggerBuilder: LoggerBuilder) {
        loggerBuilder.appendln('准备删除旧的POD.....')
        const namespace = deployConfig.envCode.toLowerCase()
        const k8sClient = this.k8sClientFactory.getClient(deployConfig.k8sId)

        let op = await k8sClient.getPodList(namespace)
        if (!op.isSuccess()) {
            loggerBuilder.appendln('获取POD列表异常:' + op.message)
            return;
        }
        const podList = op.data
        for (let pod of podList.items) {
            const name = pod.metadata.name
            const labels = pod.metadata.labels
            if (labels.deployId === deployConfig.id && labels.deployTag !== deployConfig.deployTag) {
                await this.closePod(k8sClient, namespace, name, loggerBuilder)
            }
        }

    }

    private async closePod(k8sClient, namespace: string, name: string, loggerBuilder: LoggerBuilder) {
        let op = await k8sClient.deletePod(namespace, name)
        if (op.isSuccess()) {
            loggerBuilder.appendln(`删除POD[${name}]操作成功`)
        } else {
            loggerBuilder.appendln(`删除POD[${name}]操作失败：${op.message}`)
            return op;
        }
        op = await k8sClient.getPVC(namespace, name);
        if (!op.isSuccess()) {
            return OpResult.success('OK')
        }
        op = await k8sClient.deletePVC(namespace, name)
        if (op.isSuccess()) {
            loggerBuilder.appendln(`删除PVC[${name}]操作成功`)
        } else {
            loggerBuilder.appendln(`删除PVC[${name}]操作失败：${op.message}`)
        }
        return op;
    }


}

export { K8SDeployService }