package lakala

import (
	"bytes"
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
	"encoding/json"
	"encoding/pem"
	"errors"
	"fmt"
	"git.rosy.net.cn/baseapi/utils"
	"io"
	"io/ioutil"
	"math/big"
	"mime/multipart"
	"net/http"
	"strings"
	"time"
)

// IncomingToken 进件api,token获取
func (a *API) IncomingToken() (comingToken string, expiresIn int64, err error) {
	result, err := a.AccessAPI(IncomingUrlProd, TokenActive, http.MethodPost, "", map[string]interface{}{
		"grant_type":    "client_credentials",
		"client_id":     a.clientId,
		"client_secret": a.clientSecret,
	})
	if err != nil {
		return "", 0, err
	}
	expiresIn, err = utils.TryInterface2Int64(result["expires_in"])
	if err != nil {
		return "", 0, err
	}
	a.incomingToken = result["access_token"].(string)
	a.incomingExpire = time.Now().Unix() + expiresIn
	return a.incomingToken, a.incomingExpire, nil
}

// ModifiedToken 更新token获取
func (a *API) ModifiedToken(userName string, password string) (modifiedToken string, modifiedExpire int64, err error) {
	result, err := a.AccessAPI(ModifiedUrlProd, TokenActive, http.MethodPost, "", map[string]interface{}{
		"grant_type": "password",
		"username":   userName,
		"password":   password,
	})

	if err != nil {
		return "", 0, err
	}
	modifiedExpire, err = utils.TryInterface2Int64(result["expires_in"])
	if err != nil {
		return "", 0, err
	}
	a.modifiedToken = result["access_token"].(string)
	a.modifiedExpire = time.Now().Unix() + modifiedExpire
	return a.modifiedToken, a.modifiedExpire, nil
}

// FileUpload 文件上传下载 filePath:文件url,由于controller:swagger不支持文件类型
func (a *API) FileUpload(filePath, imgType, sourcechnl, isOcr string) (*UploadImgResp, error) {
	client := &http.Client{}
	// 获取问价
	resp, err := client.Get(filePath)
	if err != nil {
		return nil, err
	}
	imgData, _ := ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()

	// 创建一个缓冲区来构建 multipart 请求
	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)

	// 添加文件到 multipart 请求
	part, err := writer.CreateFormFile("file", filePath)
	if err != nil {
		return nil, err
	}
	_, err = io.Copy(part, strings.NewReader(string(imgData)))
	if err != nil {
		return nil, err
	}

	// 添加其他表单字段
	writer.WriteField("imgType", imgType)
	writer.WriteField("sourcechnl", sourcechnl)
	writer.WriteField("isOcr", isOcr)

	// 关闭 writer
	err = writer.Close()
	if err != nil {
		return nil, err
	}

	// 创建 HTTP 请求
	req, err := http.NewRequest("POST", fmt.Sprintf("%s/%s", BaseProdUrl, FileUpload), body)
	if err != nil {
		return nil, err
	}

	// 设置 Content-Type 头
	a.CheckToken()
	req.Header.Set("Content-Type", writer.FormDataContentType())
	req.Header.Set("Authorization", fmt.Sprintf("bearer %s", a.incomingToken))

	// 发送请求
	resp, err = client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	// 读取响应
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	img := &UploadImgResp{}
	json.Unmarshal(data, img)

	return img, err
}

type UploadImgResp struct {
	BatchNo interface{} `json:"batchNo"` // 批量号
	Status  string      `json:"status"`  // 00 成功， 01 正在处理中， 02 失败
	Url     string      `json:"url"`     // 拉卡拉文件地址
	ShowUrl string      `json:"showUrl"` // 拉卡拉文件url
	Result  interface{} `json:"result"`  //
}

// AttachmentUpload 附件上传
func (a *API) AttachmentUpload(param *AttachmentImg) (*AttachmentImgResp, error) {
	reqParameter := map[string]interface{}{
		"reqData":   param,
		"ver":       Version,
		"timestamp": utils.Int64ToStr(time.Now().Unix()),
		"reqId":     utils.GetUUID(),
	}
	result, err := a.AccessAPISign(SeparateAccountProdUrl, AttachmentUpload, http.MethodPost, "", reqParameter)
	if err != nil {
		return nil, err
	}

	if result["retCode"].(string) != Success {
		return nil, errors.New(result["retMsg"].(string))
	}

	bodyResult, err := json.Marshal(result["respData"])
	if err != nil {
		return nil, err
	}

	attachment := &AttachmentImgResp{}
	if err = json.Unmarshal(bodyResult, attachment); err != nil {
		return nil, err
	}

	return attachment, nil
}

// AttachmentImg 进件附件上传
type AttachmentImg struct {
	Version    string `json:"version"`
	OrderNo    string `json:"orderNo"`
	OrgCode    string `json:"orgCode"`
	AttType    string `json:"attType"`
	AttExtName string `json:"attExtName"`
	AttContext string `json:"attContext"`
}

// AttachmentImgResp 进件返回值
type AttachmentImgResp struct {
	AttType   string `json:"attType"`
	OrderNo   string `json:"orderNo"`
	OrgCode   string `json:"orgCode"`
	AttFileId string `json:"attFileId"`
}

// signParamRSA 支付签名
func (a *API) signParamPrivateKey(params map[string]interface{}, RSAPrivate string) (sig string, err error) {
	block, _ := pem.Decode([]byte(RSAPrivate))
	private, err := x509.ParsePKCS8PrivateKey(block.Bytes)
	if err != nil {
		return "", err
	}

	// 签名参数
	body, err := json.Marshal(params)
	if err != nil {
		return "", err
	}

	//bodyData := base64.StdEncoding.EncodeToString(body)
	nonceStr := GenerateSecureRandomString(12)
	timeStamp := utils.Int64ToStr(time.Now().Unix())
	context := fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n", a.appID, a.serialNo, timeStamp, nonceStr, string(body))

	// 进行rsa加密签名
	hashed := sha256.Sum256([]byte(context))
	signedData, err := rsa.SignPKCS1v15(rand.Reader, private.(*rsa.PrivateKey), crypto.SHA256, hashed[:])
	if err != nil {
		return "", err
	}

	signData := base64.StdEncoding.EncodeToString(signedData)
	authorization := fmt.Sprintf(`LKLAPI-SHA256withRSA appid="%s",serial_no="%s",timestamp="%s",nonce_str="%s",signature="%s"`, a.appID, a.serialNo, timeStamp, nonceStr, signData)
	return authorization, nil
}

// GenerateSecureRandomString 获取随机字符串
func GenerateSecureRandomString(length int) string {
	charset := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
	bytes := make([]byte, length)
	charSetSize := big.NewInt(int64(len(charset)))
	for i := range bytes {
		num, _ := rand.Int(rand.Reader, charSetSize)
		bytes[i] = charset[num.Int64()]
	}
	return string(bytes)
}

func GetOrderNumber(length int) string {
	charset := "0123456789"
	bytes := make([]byte, length)
	charSetSize := big.NewInt(int64(len(charset)))
	for i := range bytes {
		num, _ := rand.Int(rand.Reader, charSetSize)
		bytes[i] = charset[num.Int64()]
	}

	return utils.Time2TimeStrByFormat(time.Now(), TimeFormat) + string(bytes)
}

type CallBackResult struct {
	Code    string `json:"code"`
	Message string `json:"message"`
}

// CallBackResultInfo 失败回调返回
func CallBackResultInfo(err error) *CallBackResult {
	if err == nil {
		return &CallBackResult{
			Code:    "SUCCESS",
			Message: "执行成功",
		}
	}
	return &CallBackResult{
		Code:    "400",
		Message: err.Error(),
	}
}

/****************************/

const (
	MAX_ENCRYPT_BLOCK = 117
	MAX_DECRYPT_BLOCK = 128
)

func EncryptByPrivateKey(data []byte, privateKeyStr string) (string, error) {
	// 解析私钥
	block, _ := pem.Decode([]byte(privateKeyStr))
	if block == nil {
		return "", errors.New("failed to parse PEM block")
	}

	privKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
	if err != nil {
		return "", err
	}

	rsaPrivateKey := privKey.(*rsa.PrivateKey)

	// 分段加密
	var encrypted []byte
	for offset := 0; offset < len(data); offset += MAX_ENCRYPT_BLOCK {
		end := offset + MAX_ENCRYPT_BLOCK
		if end > len(data) {
			end = len(data)
		}

		chunk, err := rsa.SignPKCS1v15(nil, rsaPrivateKey, 0, data[offset:end])
		if err != nil {
			return "", err
		}
		encrypted = append(encrypted, chunk...)
	}

	return base64.StdEncoding.EncodeToString(encrypted), nil
}

// DecryptByPublicKey 解密函数：分段解密（公钥解密，需手动实现模幂运算）
func DecryptByPublicKey(ciphertext []byte, publicKeyStr string) ([]byte, error) {
	// 解析公钥
	block, _ := pem.Decode([]byte(publicKeyStr))
	if block == nil {
		return nil, errors.New("failed to parse PEM block")
	}

	pubKey, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return nil, err
	}

	rsaPublicKey := pubKey.(*rsa.PublicKey)

	n := rsaPublicKey.N // 公钥模数
	e := rsaPublicKey.E // 公钥指数

	blockSize := rsaPublicKey.Size() // 密钥的字节长度（128字节）
	var result bytes.Buffer

	for len(ciphertext) > 0 {
		blockLen := blockSize
		if len(ciphertext) < blockLen {
			blockLen = len(ciphertext)
		}
		block := ciphertext[:blockLen]
		ciphertext = ciphertext[blockLen:]

		// 将密文块转换为大整数
		c := new(big.Int).SetBytes(block)

		// 计算 m = c^e mod n (由于私钥加密，公钥解密时需要e和n)
		m := new(big.Int).Exp(c, big.NewInt(int64(e)), n)

		// 将结果转换为字节数组，并补零到固定长度
		mBytes := m.Bytes()
		if len(mBytes) < blockSize {
			padding := make([]byte, blockSize-len(mBytes))
			mBytes = append(padding, mBytes...)
		}
		// 写入最终结果
		result.Write(mBytes)
	}

	// 移除PKCS1v15填充（需要实现填充剥离）
	// 注意：此处简化，实际需解析填充结构，比如去除00 02和填充字节
	return stripPKCS1v15Padding(result.Bytes()), nil
}

// 移除PKCS1v15填充（示例逻辑，需根据实际填充调整）
func stripPKCS1v15Padding(data []byte) []byte {
	var newData = make([]byte, 0, len(data))

	for i := 0; i < len(data); i++ {
		if data[i] == 0x01 || data[i] == 0xFF || data[i] == 0x00 {
			continue
		}
		newData = append(newData, data[i])
	}
	return newData
}
