import fs from "fs";
import { promisify } from "util";
import path from "path";
import { homedir } from "os";
import { parse } from "ini";
import { CommonClient } from "./common_client";
import CvmRoleCredential from "./cvm_role_credential";
const EXPIRE_BUFFER = 30 * 1000;
export class BasicCredential {
    constructor(secretId, secretKey, token) {
        this.secretId = secretId;
        this.secretKey = secretKey;
        this.token = token;
    }
}
export class EnvironmentVariableCredential {
    async getCredential() {
        const secretId = process.env.TENCENTCLOUD_SECRET_ID;
        const secretKey = process.env.TENCENTCLOUD_SECRET_KEY;
        const token = process.env.TENCENTCLOUD_SESSION_TOKEN;
        return new BasicCredential(secretId, secretKey, token);
    }
}
export class ProfileCredential {
    async getCredential() {
        let filePath = "";
        const userHome = homedir();
        const userCredentialsPath = path.join(userHome, ".tencentcloud", "credentials");
        if (fs.existsSync(userCredentialsPath)) {
            filePath = userCredentialsPath;
        }
        else {
            filePath = "/etc/tencentcloud/credentials";
        }
        if (filePath) {
            try {
                const content = fs.readFileSync(filePath, "utf8");
                const { default: { secret_id, secret_key, token }, } = parse(content);
                if (secret_id && secret_key) {
                    return new BasicCredential(secret_id, secret_key, token);
                }
            }
            catch (error) {
            }
        }
        return new BasicCredential("", "");
    }
}
export class STSCredential {
    constructor(clientConfig, assumeRoleParams) {
        this.clientConfig = clientConfig;
        this.assumeRoleParams = assumeRoleParams;
        this.endpoint = "sts.tencentcloudapi.com";
        this.version = "2018-08-13";
        this.action = "AssumeRole";
        this.region = "ap-guangzhou";
    }
    async getCredentialWithStsAssumeRole() {
        const { endpoint, version, action, region, clientConfig, assumeRoleParams } = this;
        try {
            const client = new CommonClient(endpoint, version, {
                region,
                ...clientConfig,
            });
            const result = await client.request(action, assumeRoleParams);
            return {
                TmpSecretId: result.Credentials.TmpSecretId,
                TmpSecretKey: result.Credentials.TmpSecretKey,
                Token: result.Credentials.Token,
                ExpiredTime: result.ExpiredTime,
                Expiration: result.Expiration,
            };
        }
        catch (error) {
            throw new Error(`Get STS AssumeRole failed: ${error.message}`);
        }
    }
    async getCredential() {
        if (!this.credentialTask) {
            this.credentialTask = this.getCredentialWithStsAssumeRole();
        }
        const credential = await this.credentialTask;
        if (credential.ExpiredTime * 1000 - EXPIRE_BUFFER <= Date.now()) {
            this.credentialTask = null;
            return this.getCredential();
        }
        return {
            secretId: credential.TmpSecretId,
            secretKey: credential.TmpSecretKey,
            token: credential.Token,
        };
    }
}
export { default as CvmRoleCredential } from "./cvm_role_credential";
export class OIDCRoleArnCredential {
    constructor(clientConfig, assumeRoleWithWebIdentityParams) {
        this.endpoint = "sts.tencentcloudapi.com";
        this.version = "2018-08-13";
        this.action = "AssumeRoleWithWebIdentity";
        this.defaultSessionName = "tencentcloud-node-sdk-";
        this.isTke = false;
        this.expirationReservationTime = 600;
        if (clientConfig && assumeRoleWithWebIdentityParams) {
            this.clientConfig = clientConfig;
            this.assumeRoleWithWebIdentityParams = assumeRoleWithWebIdentityParams;
        }
        else {
            this.isTke = true;
            this.clientConfig = {
                credential: new BasicCredential("", ""),
                ...clientConfig,
            };
        }
    }
    async initFromTke() {
        const region = process.env.TKE_REGION;
        if (!region) {
            throw new Error("env TKE_REGION not exist");
        }
        const providerId = process.env.TKE_PROVIDER_ID;
        if (!providerId) {
            throw new Error("env TKE_PROVIDER_ID not exist");
        }
        const tokenFile = process.env.TKE_WEB_IDENTITY_TOKEN_FILE;
        if (!tokenFile) {
            throw new Error("env TKE_WEB_IDENTITY_TOKEN_FILE not exist");
        }
        let wbIdentityToken;
        try {
            wbIdentityToken = (await promisify(fs.readFile)(tokenFile)).toString();
        }
        catch (error) {
            throw new Error(`failed to read token file: ${error.message}`);
        }
        const roleArn = process.env.TKE_ROLE_ARN;
        if (!roleArn) {
            throw new Error("env TKE_ROLE_ARN not exist");
        }
        this.clientConfig.region = region;
        this.assumeRoleWithWebIdentityParams = {
            RoleArn: roleArn,
            RoleSessionName: `${this.defaultSessionName}${Date.now() * 1000}`,
            WebIdentityToken: wbIdentityToken,
            ProviderId: providerId,
        };
    }
    async getCredentialWithStsAssumeRoleWithWebIdentity() {
        try {
            if (this.isTke) {
                await this.initFromTke();
            }
            const { endpoint, version, action, region, clientConfig, assumeRoleWithWebIdentityParams } = this;
            const client = new CommonClient(endpoint, version, {
                region: region,
                ...clientConfig,
            });
            const result = await client.request(action, assumeRoleWithWebIdentityParams, {
                skipSign: true
            });
            return {
                TmpSecretId: result.Credentials.TmpSecretId,
                TmpSecretKey: result.Credentials.TmpSecretKey,
                Token: result.Credentials.Token,
                ExpiredTime: result.ExpiredTime,
                Expiration: result.Expiration,
            };
        }
        catch (error) {
            throw new Error(`Get STS AssumeRoleWithWebIdentity failed: ${error.message}`);
        }
    }
    async getCredential() {
        if (!this.credentialTask) {
            this.credentialTask = this.getCredentialWithStsAssumeRoleWithWebIdentity();
        }
        const credential = await this.credentialTask;
        if (credential.ExpiredTime * 1000 - this.expirationReservationTime <= Date.now()) {
            this.credentialTask = null;
            return this.getCredential();
        }
        return {
            secretId: credential.TmpSecretId,
            secretKey: credential.TmpSecretKey,
            token: credential.Token,
        };
    }
}
export class DefaultCredentialProvider {
    constructor() {
        this.providers = [
            new EnvironmentVariableCredential(),
            new ProfileCredential(),
            new CvmRoleCredential(),
            new OIDCRoleArnCredential(),
        ];
    }
    async getCredential() {
        for (const provider of this.providers) {
            try {
                const credential = await provider.getCredential();
                if (credential.secretId && credential.secretKey) {
                    return credential;
                }
            }
            catch (error) {
                console.error(error);
                continue;
            }
        }
        return new BasicCredential("", "");
    }
}
