package request

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"net/http"
	"strings"
	"syno-common/server/utils"
	"time"
)

type Option struct {
	Url       string
	Resp      any
	Accept    string
	Headers   map[string]string
	ApiKey    string
	ApiSecret string
	Sign      func(option Option) map[string]string
}

// Request 封装请求库
type Request struct {
	BaseURL string
	Timeout time.Duration
}

func defaultSign(option Option) map[string]string {
	if option.Headers == nil {
		option.Headers = map[string]string{}
	}

	apiKey := option.ApiKey
	apiSecret := option.ApiSecret
	timestamp := time.Now().Unix()
	nonce := utils.RandString(16)

	fmt.Printf("key: %v, secret: %v,time: %v,nonce: %v\n", apiKey, apiSecret, timestamp, nonce)

	cipherText := fmt.Sprintf("%v%v%v", apiKey, timestamp, nonce)

	sign, err := utils.AesEncrypt(cipherText, apiSecret)
	if err != nil {
		log.Printf("sign fail: %v", err)
	} else {
		option.Headers["x-api-key"] = apiKey
		option.Headers["x-api-timestamp"] = fmt.Sprintf("%v", timestamp)
		option.Headers["x-api-nonce"] = nonce
		option.Headers["x-api-sign"] = sign
		log.Printf("sign ok: %v", sign)
	}
	//log.Printf("headers: %v", option.Headers)
	return option.Headers
}

func NewRequest(baseUrl string, timeout time.Duration) Request {
	return Request{
		BaseURL: baseUrl,
		Timeout: timeout,
	}
}

func (r *Request) DoPostJson(requestData any, option Option) (any, error) {
	data, err := json.Marshal(requestData)
	if err != nil {
		return nil, err
	}
	url := fmt.Sprintf("%s%s", r.BaseURL, option.Url)
	if !strings.HasPrefix(option.Url, "/") && !strings.HasSuffix(r.BaseURL, "/") {
		url = fmt.Sprintf("%s/%s", r.BaseURL, option.Url)
	}

	req, err := http.NewRequest("POST", url, bytes.NewReader(data))
	if err != nil {
		return nil, err
	}

	userHeaders := option.Headers
	if option.ApiKey != "" {
		if option.Sign == nil {
			userHeaders = defaultSign(option)
		} else {
			userHeaders = option.Sign(option)
		}
	}

	log.Printf("headers: %v", userHeaders)
	req.Header.Set("Content-Type", "application/json")
	if userHeaders == nil {
		userHeaders = map[string]string{}
	}
	for header := range userHeaders {
		req.Header.Set(header, userHeaders[header])
	}

	client := &http.Client{
		Timeout: r.Timeout,
	}
	rsp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	if rsp.StatusCode < 200 || rsp.StatusCode > 299 {
		err = errors.New(rsp.Status)
		log.Printf("status: %v", rsp.StatusCode)
		return nil, err
	}
	body, err := io.ReadAll(rsp.Body)
	if err != nil {
		log.Printf("read body fail: %v", err)
		return nil, errors.New("body parse fail")
	}

	if option.Resp != nil {
		err := json.Unmarshal(body, option.Resp)
		if err != nil {
			log.Printf("parse body fail: %v", err)
			return nil, err
		}
		return option.Resp, nil
	}
	if option.Accept == "" || option.Accept == "application/json" {
		var respData = map[string]interface{}{}
		err := json.Unmarshal(body, &respData)
		//log.Printf("resp data: %v", respData)
		if err != nil {
			log.Printf("parse body fail: %v", err)
			return nil, err
		}
		return respData, nil
	}
	return body, nil
}

func Test(apiKey string, secret string) {
	request := NewRequest("http://10.10.0.1:20003", time.Second*10)

	data := map[string]interface{}{}

	option := Option{
		Url:       "/client/login",
		Resp:      nil,
		Accept:    "",
		ApiKey:    apiKey,
		ApiSecret: secret,
	}

	resp, err := request.DoPostJson(data, option)
	if err != nil {
		log.Fatalln(err)
	}
	log.Printf("resp: %v", resp)
}
