import { IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { utils } from '@certd/basic';

import dayjs from 'dayjs';
import { AbstractPlusTaskPlugin } from '@certd/plugin-plus';

@IsTaskPlugin({
  name: 'DeployCertToTencentTKEIngress',
  title: '腾讯云-部署到TKE-ingress',
  needPlus: true,
  icon: 'svg:icon-tencentcloud',
  group: pluginGroups.tencent.key,
  desc: 'serverless集群请使用K8S部署插件；Qcloud类型需要【上传到腾讯云】作为前置任务；ApiServer未开启外网访问则需要做域名的内网IP映射',
  default: {
    strategy: {
      runStrategy: RunStrategy.SkipWhenSucceed,
    },
  },
})
export class DeployCertToTencentTKEIngressPlugin extends AbstractPlusTaskPlugin {
  @TaskInput({ title: '大区', value: 'ap-guangzhou', required: true })
  region!: string;

  @TaskInput({
    title: '集群ID',
    required: true,
    desc: '例如：cls-6lbj1vee',
    request: true,
  })
  clusterId!: string;

  @TaskInput({ title: '集群namespace', value: 'default', required: true })
  namespace!: string;

  @TaskInput({ title: '证书的secret名称', required: true })
  secretName!: string | string[];

  @TaskInput({ title: 'ingress名称', required: true })
  ingressName!: string | string[];

  @TaskInput({
    title: 'ingress类型',
    component: {
      name: 'a-auto-complete',
      vModel: 'value',
      options: [{ value: 'qcloud' }, { value: 'nginx' }],
    },
    helper: '可选 qcloud / nginx',
  })
  ingressClass!: string;

  // @TaskInput({ title: "集群内网ip", helper: "如果开启了外网的话，无需设置" })
  // clusterIp!: string;

  @TaskInput({
    title: '集群域名',
    helper: '可不填，默认为:[clusterId].ccs.tencent-cloud.com',
  })
  clusterDomain!: string;

  /**
   * AccessProvider的key,或者一个包含access的具体的对象
   */
  @TaskInput({
    title: 'Access授权',
    helper: 'access授权',
    component: {
      name: 'access-selector',
      type: 'tencent',
    },
    required: true,
  })
  accessId!: string;

  @TaskInput({
    title: '腾讯云证书id',
    helper: '请选择“上传证书到腾讯云”前置任务的输出',
    component: {
      name: 'output-selector',
      from: 'UploadCertToTencent',
    },
    mergeScript: `
      return {
        show: ctx.compute(({form})=>{
          return form.ingressClass === "qcloud"
        })
      }
    `,
    required: true,
  })
  tencentCertId!: string;

  @TaskInput({
    title: '域名证书',
    helper: '请选择前置任务输出的域名证书',
    component: {
      name: 'output-selector',
      from: ['CertApply', 'CertApplyLego'],
    },
    mergeScript: `
      return {
        show: ctx.compute(({form})=>{
          return form.ingressClass === "nginx"
        })
      }
    `,
    required: true,
  })
  cert!: any;

  K8sClient: any;

  async onInstance() {
    //  const TkeClient = this.tencentcloud.tke.v20180525.Client;
    const k8sSdk = await import('@certd/lib-k8s');
    this.K8sClient = k8sSdk.K8sClient;
  }
  async execute(): Promise<void> {
    const accessProvider = await this.accessService.getById(this.accessId);
    const tkeClient = await this.getTkeClient(accessProvider, this.region);
    const kubeConfigStr = await this.getTkeKubeConfig(tkeClient, this.clusterId);

    this.logger.info('kubeconfig已成功获取');
    const k8sClient = new this.K8sClient({
      kubeConfigStr,
      logger: this.logger,
    });
    // if (this.clusterIp != null) {
    //   if (!this.clusterDomain) {
    //     this.clusterDomain = `${this.clusterId}.ccs.tencent-cloud.com`;
    //   }
    //   // 修改内网解析ip地址
    //   k8sClient.setLookup({ [this.clusterDomain]: { ip: this.clusterIp } });
    // }
    const ingressType = this.ingressClass || 'qcloud';
    if (ingressType === 'qcloud') {
      await this.patchQcloudCertSecret({ k8sClient });
    } else {
      await this.patchNginxCertSecret({ k8sClient });
    }

    await utils.sleep(5000); // 停留2秒，等待secret部署完成
    await this.restartIngress({ k8sClient });
  }

  async getTkeClient(accessProvider: any, region = 'ap-guangzhou') {
    const sdk = await import('tencentcloud-sdk-nodejs/tencentcloud/services/tke/v20180525/index.js');
    const TkeClient = sdk.v20180525.Client;
    const clientConfig = {
      credential: {
        secretId: accessProvider.secretId,
        secretKey: accessProvider.secretKey,
      },
      region,
      profile: {
        httpProfile: {
          endpoint: 'tke.tencentcloudapi.com',
        },
      },
    };

    return new TkeClient(clientConfig);
  }

  async getTkeKubeConfig(client: any, clusterId: string) {
    // Depends on tencentcloud-sdk-nodejs version 4.0.3 or higher
    const params = {
      ClusterId: clusterId,
    };
    const ret = await client.DescribeClusterKubeconfig(params);
    this.checkRet(ret);
    this.logger.info('注意：后续操作需要在【集群->基本信息】中开启外网或内网访问,https://console.cloud.tencent.com/tke2/cluster');
    return ret.Kubeconfig;
  }

  appendTimeSuffix(name: string) {
    if (name == null) {
      name = 'certd';
    }
    return name + '-' + dayjs().format('YYYYMMDD-HHmmss');
  }

  async patchQcloudCertSecret(options: { k8sClient: any }) {
    if (this.tencentCertId == null) {
      throw new Error('请先将【上传证书到腾讯云】作为前置任务');
    }
    this.logger.info('腾讯云证书ID:', this.tencentCertId);
    const certIdBase64 = Buffer.from(this.tencentCertId).toString('base64');

    const { namespace, secretName } = this;

    const body = {
      data: {
        qcloud_cert_id: certIdBase64,
      },
      metadata: {
        labels: {
          certd: this.appendTimeSuffix('certd'),
        },
      },
    };
    let secretNames: any = secretName;
    if (typeof secretName === 'string') {
      secretNames = [secretName];
    }
    for (const secret of secretNames) {
      await options.k8sClient.patchSecret({
        namespace,
        secretName: secret,
        body,
      });
      this.logger.info(`CertSecret已更新:${secret}`);
    }
  }

  async patchNginxCertSecret(options: { k8sClient: any }) {
    const { k8sClient } = options;
    const { cert } = this;
    const crt = cert.crt;
    const key = cert.key;
    const crtBase64 = Buffer.from(crt).toString('base64');
    const keyBase64 = Buffer.from(key).toString('base64');

    const { namespace, secretName } = this;

    const body = {
      data: {
        'tls.crt': crtBase64,
        'tls.key': keyBase64,
      },
      metadata: {
        labels: {
          certd: this.appendTimeSuffix('certd'),
        },
      },
    };
    let secretNames = secretName;
    if (typeof secretName === 'string') {
      secretNames = [secretName];
    }
    for (const secret of secretNames) {
      await k8sClient.patchSecret({ namespace, secretName: secret, body });
      this.logger.info(`CertSecret已更新:${secret}`);
    }
  }

  async restartIngress(options: { k8sClient: any }) {
    const { k8sClient } = options;
    const { namespace, ingressName } = this;

    const body = {
      metadata: {
        labels: {
          certd: this.appendTimeSuffix('certd'),
        },
      },
    };
    let ingressNames = this.ingressName;
    if (typeof ingressName === 'string') {
      ingressNames = [ingressName];
    }
    for (const ingress of ingressNames) {
      await k8sClient.patchIngress({ namespace, ingressName: ingress, body });
      this.logger.info(`ingress已重启:${ingress}`);
    }
  }
  checkRet(ret: any) {
    if (!ret || ret.Error) {
      throw new Error('执行失败：' + ret.Error.Code + ',' + ret.Error.Message);
    }
  }
}
