import { IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { AbstractPlusTaskPlugin, TencentAccess } from '@certd/plugin-plus';
import { TencentSslClient } from '../../lib/index.js';
import dayjs from 'dayjs';
import { remove } from 'lodash-es';

@IsTaskPlugin({
  name: 'TencentDeleteExpiringCert',
  title: '删除腾讯云即将过期证书',
  icon: 'svg:icon-tencentcloud',
  group: pluginGroups.tencent.key,
  desc: '仅删除未使用的证书',
  default: {
    strategy: {
      runStrategy: RunStrategy.AlwaysRun,
    },
  },
  needPlus: true,
})
export class TencentDeleteExpiringCert extends AbstractPlusTaskPlugin {
  @TaskInput({
    title: 'Access提供者',
    helper: 'access 授权',
    component: {
      name: 'access-selector',
      type: 'tencent',
    },
    required: true,
  })
  accessId!: string;

  @TaskInput({
    title: '关键字筛选',
    helper: '仅匹配ID、备注名称、域名包含关键字的证书，可以不填',
    required: false,
    component: {
      name: 'a-input',
    },
  })
  searchKey!: string;

  @TaskInput({
    title: '最大删除数量',
    helper: '单次运行最大删除数量',
    value: 100,
    component: {
      name: 'a-input-number',
      vModel: 'value',
    },
    required: true,
  })
  maxCount!: number;

  @TaskInput({
    title: '即将过期天数',
    helper:
      '仅删除有效期小于此天数的证书,\n<span class="color-red">注意：`1.26.14`版本之前Certd创建的证书流水线默认是到期前20天才更新证书，需要将之前创建的证书申请任务的更新天数改为35天，保证删除之前就已经替换掉即将过期证书</span>',
    value: 30,
    component: {
      name: 'a-input-number',
      vModel: 'value',
    },
    required: true,
  })
  expiringDays!: number;

  @TaskInput({
    title: '检查超时时间',
    helper: '检查删除任务结果超时时间,单位分钟',
    value: 10,
    component: {
      name: 'a-input-number',
      vModel: 'value',
    },
    required: true,
  })
  checkTimeout!: number;

  async onInstance() {}

  async execute(): Promise<void> {
    const access = await this.accessService.getById<TencentAccess>(this.accessId);
    const sslClient = new TencentSslClient({
      access,
      logger: this.logger,
    });

    const params = {
      Limit: this.maxCount ?? 100,
      SearchKey: this.searchKey,
      ExpirationSort: 'ASC',
      FilterSource: 'upload',
      // FilterExpiring: 1,
    };
    const res = await sslClient.DescribeCertificates(params);
    let certificates = res?.Certificates;
    if (!certificates && certificates.length === 0) {
      this.logger.info('没有找到证书');
      return;
    }

    const lastDay = dayjs().add(this.expiringDays, 'day');
    certificates = certificates.filter((item: any) => {
      const endTime = item.CertEndTime;
      return dayjs(endTime).isBefore(lastDay);
    });
    for (const certificate of certificates) {
      this.logger.info(`证书ID:${certificate.CertificateId}, 过期时间:${certificate.CertEndTime}，Alias:${certificate.Alias}，证书域名:${certificate.Domain}`);
    }
    this.logger.info(`即将过期的证书数量:${certificates.length}`);
    if (certificates.length === 0) {
      this.logger.info('没有即将过期的证书, 无需删除');
      return;
    }
    const certIds = certificates.map((cert: any) => cert.CertificateId);

    const deleteRes = await sslClient.doRequest('DeleteCertificates', {
      CertificateIds: certIds,
      IsSync: true,
    });
    this.logger.info('删除任务已提交: ', JSON.stringify(deleteRes));
    const ids = deleteRes?.CertTaskIds;
    if (!ids && !ids.length) {
      this.logger.error('没有找到任务ID');
      return;
    }
    const taskIds = ids.map((id: any) => id.TaskId);
    const startTime = Date.now();
    const results = {};

    const statusCount = {
      success: 0,
      failed: 0,
      unauthorized: 0,
      unbind: 0,
      timeout: 0,
    };
    const total = taskIds.length;

    while (Date.now() < startTime + this.checkTimeout * 60 * 1000) {
      this.checkSignal();
      const taskResultRes = await sslClient.doRequest('DescribeDeleteCertificatesTaskResult', {
        TaskIds: taskIds,
      });
      const result = taskResultRes.DeleteTaskResult;
      if (!result || result.length === 0) {
        this.logger.info('暂未获取到有效的任务结果');
        continue;
      }
      for (const item of result) {
        //遍历结果
        const status = item.Status;
        if (status !== 0) {
          remove(taskIds, id => id === item.TaskId);
        }
        // Status : 0表示任务进行中、 1表示任务成功、 2表示任务失败、3表示未授权服务角色导致任务失败、4表示有未解绑的云资源导致任务失败、5表示查询关联云资源超时导致任务失败
        if (status === 0) {
          this.logger.info(`任务${item.TaskId}<${item.CertId}>: 进行中`);
        } else if (status === 1) {
          this.logger.info(`任务${item.TaskId}<${item.CertId}>: 成功`);
          results[item.TaskId] = '成功';
          statusCount.success++;
        } else if (status === 2) {
          this.logger.error(`任务${item.TaskId}<${item.CertId}>: 失败`);
          results[item.TaskId] = '失败';
          statusCount.failed++;
        } else if (status === 3) {
          this.logger.error(`任务${item.TaskId}<${item.CertId}>: 未授权服务角色导致任务失败`);
          results[item.TaskId] = '未授权服务角色导致任务失败';
          statusCount.unauthorized++;
        } else if (status === 4) {
          this.logger.error(`任务${item.TaskId}<${item.CertId}>: 有未解绑的云资源导致任务失败`);
          results[item.TaskId] = '有未解绑的云资源导致任务失败';
          statusCount.unbind++;
        } else if (status === 5) {
          this.logger.error(`任务${item.TaskId}<${item.CertId}>: 查询关联云资源超时导致任务失败`);
          results[item.TaskId] = '查询关联云资源超时导致任务失败';
          statusCount.timeout++;
        } else {
          this.logger.info(`任务${item.TaskId}<${item.CertId}>: 未知状态:${status}`);
          statusCount.failed++;
        }
      }
      this.logger.info(
        // eslint-disable-next-line max-len
        `任务总数:${total}, 进行中：${taskIds.length}， 成功:${statusCount.success}, 未授权服务角色导致失败:${statusCount.unauthorized}, 未解绑关联资源失败:${statusCount.unbind}, 查询关联资源超时:${statusCount.timeout}，未知原因失败:${statusCount.failed}`
      );
      if (taskIds.length === 0) {
        this.logger.info('任务已全部完成');

        if (statusCount.unauthorized > 0) {
          throw new Error('有未授权服务角色导致任务失败，需给Access授权服务角色SSL_QCSLinkedRoleInReplaceLoadCertificate');
        }

        return;
      }
      await this.ctx.utils.sleep(10000);
    }
    this.logger.error('检查任务结果超时', JSON.stringify(results));
  }
}

new TencentDeleteExpiringCert();
