import { logger, safePromise, utils } from "@certd/basic";
import { merge } from "lodash-es";
import https from "https";
import { PeerCertificate } from "tls";

export type SiteTestReq = {
  host: string; // 只用域名部分
  port?: number;
  method?: string;
  retryTimes?: number;
  ipAddress?: string;
};

export type SiteTestRes = {
  certificate?: PeerCertificate;
};

export class SiteTester {
  async test(req: SiteTestReq): Promise<SiteTestRes> {
    logger.info("测试站点:", JSON.stringify(req));
    const maxRetryTimes = req.retryTimes==null ? 3 : req.retryTimes;
    let tryCount = 0;
    let result: SiteTestRes = {};
    while (true) {
      try {
        result = await this.doTestOnce(req);
        return result;
      } catch (e) {
        tryCount++;
        if (tryCount > maxRetryTimes) {
          logger.error(`测试站点出错，已超过最大重试次数（${maxRetryTimes}）`, e.message);
          throw e;
        }
        //指数退避
        const time = 2 ** tryCount;
        logger.error(`测试站点出错，${time}s后重试(${tryCount}/${maxRetryTimes})`, e);
        await utils.sleep(time * 1000);
      }
    }
  }

  async doTestOnce(req: SiteTestReq): Promise<SiteTestRes> {
    const options: any = merge(
      {
        port: 443,
        method: "GET",
        rejectUnauthorized: false
      },
      req
    );

    if (req.ipAddress) {
      //使用固定的ip
      const ipAddress = req.ipAddress;
      options.headers={
        host: options.host,
        //sni
        servername: options.host
      }
      options.host = ipAddress;
    }

    options.agent = new https.Agent({ keepAlive: false });

    // 创建 HTTPS 请求
    const requestPromise = safePromise((resolve, reject) => {
      const req = https.request(options, res => {
        // 获取证书
        // @ts-ignore
        const certificate = res.socket.getPeerCertificate();
        // logger.info('证书信息', certificate);
        if (certificate.subject == null) {
          logger.warn("证书信息为空");
          resolve({
            certificate: null
          });
        }
        resolve({
          certificate
        });
        res.socket.end();
        // 关闭响应
        res.destroy();
      });

      req.on("error", e => {
        reject(e);
      });
      req.end();
    });

    return await requestPromise;
  }
}

export const siteTester = new SiteTester();
