package ddns

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io"
	"log"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/naiba/nezha/pkg/utils"
)

const te = "https://dnspod.tencentcloudapi.com"

type ProviderTencentCloud struct {
	secretID     string
	secretKey    string
	domainConfig *DomainConfig
	resp         *tcResp
}

type tcReq struct {
	RecordType string `json:"RecordType"`
	Domain     string `json:"Domain"`
	RecordLine string `json:"RecordLine"`
	Subdomain  string `json:"Subdomain,omitempty"`
	SubDomain  string `json:"SubDomain,omitempty"` // As is
	Value      string `json:"Value,omitempty"`
	TTL        uint32 `json:"TTL,omitempty"`
	RecordId   uint64 `json:"RecordId,omitempty"`
}

type tcResp struct {
	Response struct {
		RecordList []struct {
			RecordId uint64
			Value    string
		}
		Error struct {
			Code string
		}
	}
}

func NewProviderTencentCloud(id, key string) *ProviderTencentCloud {
	return &ProviderTencentCloud{
		secretID:  id,
		secretKey: key,
	}
}

func (provider *ProviderTencentCloud) UpdateDomain(domainConfig *DomainConfig) error {
	if domainConfig == nil {
		return fmt.Errorf("获取 DDNS 配置失败")
	}
	provider.domainConfig = domainConfig

	// 当IPv4和IPv6同时成功才算作成功
	var err error
	if provider.domainConfig.EnableIPv4 {
		if err = provider.addDomainRecord(true); err != nil {
			return err
		}
	}

	if provider.domainConfig.EnableIpv6 {
		if err = provider.addDomainRecord(false); err != nil {
			return err
		}
	}

	return err
}

func (provider *ProviderTencentCloud) addDomainRecord(isIpv4 bool) error {
	err := provider.findDNSRecord(isIpv4)
	if err != nil {
		return fmt.Errorf("查找 DNS 记录时出错: %s", err)
	}

	if provider.resp.Response.Error.Code == "ResourceNotFound.NoDataOfRecord" { // 没有找到 DNS 记录
		return provider.createDNSRecord(isIpv4)
	} else if provider.resp.Response.Error.Code != "" {
		return fmt.Errorf("查询 DNS 记录时出错,错误代码为: %s", provider.resp.Response.Error.Code)
	}

	// 默认情况下更新 DNS 记录
	return provider.updateDNSRecord(isIpv4)
}

func (provider *ProviderTencentCloud) findDNSRecord(isIPv4 bool) error {
	var ipType string
	if isIPv4 {
		ipType = "A"
	} else {
		ipType = "AAAA"
	}

	prefix, realDomain := splitDomain(provider.domainConfig.FullDomain)
	data := &tcReq{
		RecordType: ipType,
		Domain:     realDomain,
		RecordLine: "默认",
		Subdomain:  prefix,
	}

	jsonData, _ := utils.Json.Marshal(data)
	body, err := provider.sendRequest("DescribeRecordList", jsonData)
	if err != nil {
		return err
	}

	provider.resp = &tcResp{}
	err = utils.Json.Unmarshal(body, provider.resp)
	if err != nil {
		return err
	}

	return nil
}

func (provider *ProviderTencentCloud) createDNSRecord(isIPv4 bool) error {
	var ipType, ipAddr string
	if isIPv4 {
		ipType = "A"
		ipAddr = provider.domainConfig.Ipv4Addr
	} else {
		ipType = "AAAA"
		ipAddr = provider.domainConfig.Ipv6Addr
	}

	prefix, realDomain := splitDomain(provider.domainConfig.FullDomain)
	data := &tcReq{
		RecordType: ipType,
		RecordLine: "默认",
		Domain:     realDomain,
		SubDomain:  prefix,
		Value:      ipAddr,
		TTL:        600,
	}

	jsonData, _ := utils.Json.Marshal(data)
	_, err := provider.sendRequest("CreateRecord", jsonData)
	return err
}

func (provider *ProviderTencentCloud) updateDNSRecord(isIPv4 bool) error {
	var ipType, ipAddr string
	if isIPv4 {
		ipType = "A"
		ipAddr = provider.domainConfig.Ipv4Addr
	} else {
		ipType = "AAAA"
		ipAddr = provider.domainConfig.Ipv6Addr
	}

	prefix, realDomain := splitDomain(provider.domainConfig.FullDomain)
	data := &tcReq{
		RecordType: ipType,
		RecordLine: "默认",
		Domain:     realDomain,
		SubDomain:  prefix,
		Value:      ipAddr,
		TTL:        600,
		RecordId:   provider.resp.Response.RecordList[0].RecordId,
	}

	jsonData, _ := utils.Json.Marshal(data)
	_, err := provider.sendRequest("ModifyRecord", jsonData)
	return err
}

// 以下为辅助方法,如发送 HTTP 请求等
func (provider *ProviderTencentCloud) sendRequest(action string, data []byte) ([]byte, error) {
	req, err := http.NewRequest("POST", te, bytes.NewBuffer(data))
	if err != nil {
		return nil, err
	}

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-TC-Version", "2021-03-23")

	provider.signRequest(provider.secretID, provider.secretKey, req, action, string(data))
	resp, err := utils.HttpClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer func(Body io.ReadCloser) {
		err := Body.Close()
		if err != nil {
			log.Printf("NEZHA>> 无法关闭HTTP响应体流: %s\n", err.Error())
		}
	}(resp.Body)

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	return body, nil
}

// https://github.com/jeessy2/ddns-go/blob/master/util/tencent_cloud_signer.go

func (provider *ProviderTencentCloud) sha256hex(s string) string {
	b := sha256.Sum256([]byte(s))
	return hex.EncodeToString(b[:])
}

func (provider *ProviderTencentCloud) hmacsha256(s, key string) string {
	hashed := hmac.New(sha256.New, []byte(key))
	hashed.Write([]byte(s))
	return string(hashed.Sum(nil))
}

func (provider *ProviderTencentCloud) WriteString(strs ...string) string {
	var b strings.Builder
	for _, str := range strs {
		b.WriteString(str)
	}

	return b.String()
}

func (provider *ProviderTencentCloud) signRequest(secretId string, secretKey string, r *http.Request, action string, payload string) {
	algorithm := "TC3-HMAC-SHA256"
	service := "dnspod"
	host := provider.WriteString(service, ".tencentcloudapi.com")
	timestamp := time.Now().Unix()
	timestampStr := strconv.FormatInt(timestamp, 10)

	// 步骤 1:拼接规范请求串
	canonicalHeaders := provider.WriteString("content-type:application/json\nhost:", host, "\nx-tc-action:", strings.ToLower(action), "\n")
	signedHeaders := "content-type;host;x-tc-action"
	hashedRequestPayload := provider.sha256hex(payload)
	canonicalRequest := provider.WriteString("POST\n/\n\n", canonicalHeaders, "\n", signedHeaders, "\n", hashedRequestPayload)

	// 步骤 2:拼接待签名字符串
	date := time.Unix(timestamp, 0).UTC().Format("2006-01-02")
	credentialScope := provider.WriteString(date, "/", service, "/tc3_request")
	hashedCanonicalRequest := provider.sha256hex(canonicalRequest)
	string2sign := provider.WriteString(algorithm, "\n", timestampStr, "\n", credentialScope, "\n", hashedCanonicalRequest)

	// 步骤 3:计算签名
	secretDate := provider.hmacsha256(date, provider.WriteString("TC3", secretKey))
	secretService := provider.hmacsha256(service, secretDate)
	secretSigning := provider.hmacsha256("tc3_request", secretService)
	signature := hex.EncodeToString([]byte(provider.hmacsha256(string2sign, secretSigning)))

	// 步骤 4:拼接 Authorization
	authorization := provider.WriteString(algorithm, " Credential=", secretId, "/", credentialScope, ", SignedHeaders=", signedHeaders, ", Signature=", signature)

	r.Header.Add("Authorization", authorization)
	r.Header.Set("Host", host)
	r.Header.Set("X-TC-Action", action)
	r.Header.Add("X-TC-Timestamp", timestampStr)
}