import { readFileSync } from 'fs'
import * as crypto from 'crypto'
import axios from 'axios'
import { format, addSeconds } from 'date-fns'
import * as qs from 'querystring'
import * as path from 'path'
import { InternalServerErrorException, Logger } from '@nestjs/common'

const TAG = 'SandPay'

const privateKey = crypto.createPrivateKey({
    key: readFileSync(path.join(__dirname, '..', '..', 'certs', '6888806043057.key'))
})

const publicKey = crypto.createPublicKey({
    key: readFileSync(path.join(__dirname, '..', '..', 'certs', 'sand.crt'))
})

function privSign(str) {
    const signer = crypto.createSign('SHA1')
    signer.update(str)
    signer.end()
    const signature = signer.sign(privateKey)
    return signature.toString('base64')
}

function verifySign(str, sign) {
    const verifier = crypto.createVerify('SHA1')
    verifier.update(str)
    verifier.end()
    return verifier.verify(publicKey, Buffer.from(sign.replace(/\s/g, '+'), 'base64'))
}

async function request(header, body, url) {
    const data = {
        head: header,
        body
    }
    const dataStr = JSON.stringify(data)
    console.log(JSON.stringify(data, null, 4))
    const sign = await privSign(dataStr)
    const postBody = {
        charset: 'UTF-8',
        data: dataStr,
        signType: '01',
        sign,
        extend: ''
    }
    console.log(qs.stringify(postBody))
    return await axios.post(url, qs.stringify(postBody), {
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
        },
        timeout: 30000,
        timeoutErrorMessage: '请求超时'
    })
}
const algorithm = 'aes-128-ecb'
async function withdraw(name, bank, amount) {
    const data = {
        cityNo: '',
        productId: '00000004',
        bankType: '',
        payMode: '',
        accNo: bank,
        accName: name,
        bankName: '',
        remark: '消费',
        channelType: '',
        accAttr: '0',
        version: '01',
        timeOut: format(addSeconds(new Date(), 180), 'yyyyMMddHHmmss'),
        extend: '',
        extendParams: '',
        tranTime: format(new Date(), 'yyyyMMddHHmmss'),
        provNo: '',
        phone: '',
        tranAmt: (amount * 100).toFixed(0).padStart(12, '0'),
        reqReserved: '',
        orderCode: new Date().getTime() + '',
        accType: '4',
        currencyCode: '156'
    }
    const dataStr = JSON.stringify(data)

    // 加密密钥，必须是 16、24 或 32 个字符长（分别对应 AES-128、AES-192 或 AES-256）
    const key = crypto.randomBytes(16)

    // 加密算法使用 AES-256-ECB

    // 将要加密的数据转换为一个 Buffer 对象
    const dataBuffer = Buffer.from(dataStr, 'utf8')

    // 创建一个加密器对象，使用 PKCS5Padding 进行补位
    const cipher = crypto.createCipheriv(algorithm, key, null)
    cipher.setAutoPadding(true)

    // 加密数据，并返回加密后的 Buffer 对象
    const encryptedBuffer = Buffer.concat([cipher.update(dataBuffer), cipher.final()])

    // 将加密后的 Buffer 对象转换为十六进制字符串
    const encrypted = encryptedBuffer.toString('base64')

    const sign = await privSign(dataStr)

    const encryptKey = crypto
        .publicEncrypt(
            {
                key: publicKey,
                padding: crypto.constants.RSA_PKCS1_PADDING
            },
            key
        )
        .toString('base64')

    const body = {
        transCode: 'RTPM',
        accessType: '0',
        merId: '6888806043057',
        encryptKey: encryptKey,
        encryptData: encrypted,
        sign,
        extend: ''
    }
    const res = await axios.post('http://120.78.171.194:11223/agent-main/openapi/agentpay', qs.stringify(body))

    const retData = qs.parse(qs.unescape(res.data))

    const decryptKey = crypto.privateDecrypt(
        {
            key: privateKey,
            padding: crypto.constants.RSA_PKCS1_PADDING
        },
        Buffer.from((retData.encryptKey as string).replace(/\s/g, '+'), 'base64')
    )

    const decipher = crypto.createDecipheriv(algorithm, decryptKey, null)
    decipher.setAutoPadding(true)

    const decryptedBuffer = Buffer.concat([
        decipher.update(Buffer.from((retData.encryptData as string).replace(/\s/g, '+'), 'base64')),
        decipher.final()
    ])
    const decrypted = decryptedBuffer.toString('utf8')

    const verify = verifySign(decrypted, retData.sign)

    if (!verify) {
        throw new InternalServerErrorException('验签失败')
    }

    const ret = JSON.parse(decrypted)

    Logger.log(`withdraw resp=${JSON.stringify(ret, null, 4)}`, TAG)
    if (ret.respCode !== '0000') {
        throw new InternalServerErrorException(ret.respDesc)
    }
}
export { withdraw }
