package http_client

import (
	"encoding/json"
	"github.com/go-resty/resty/v2"
	"github.com/pkg/errors"
)

type RequestOption func(request *Request)

func WithRequestHeaders(headers map[string]string) RequestOption {
	return func(request *Request) {
		request.SetHeaders(headers)
	}
}

func WithRequestQueryParams(queryParams map[string]string) RequestOption {
	return func(request *Request) {
		request.SetQueryParams(queryParams)
	}
}

func WithRequestPathParams(pathParams map[string]string) RequestOption {
	return func(request *Request) {
		request.SetPathParams(pathParams)
	}
}

func WithRequestFiles(files map[string]string) RequestOption {
	return func(request *Request) {
		request.SetFiles(files)
	}
}

func WithRequestFormData(data map[string]string) RequestOption {
	return func(request *Request) {
		request.SetFormData(data)
	}
}

type Request struct {
	request *resty.Request
}

func (request *Request) SetHeaders(headers map[string]string) {
	if headers == nil || len(headers) == 0 {
		return
	}

	request.request.SetHeaders(headers)
}

func (request *Request) SetQueryParams(queryParams map[string]string) {
	if queryParams == nil || len(queryParams) == 0 {
		return
	}

	request.request.SetQueryParams(queryParams)
}

func (request *Request) SetPathParams(pathParams map[string]string) {
	if pathParams == nil || len(pathParams) == 0 {
		return
	}

	request.request.SetPathParams(pathParams)
}

func (request *Request) SetFiles(files map[string]string) {
	if files == nil || len(files) == 0 {
		return
	}

	request.request.SetFiles(files)
}

func (request *Request) SetFormData(data map[string]string) {
	if data == nil || len(data) == 0 {
		return
	}

	request.request.SetFormData(data)
}

func (request *Request) Post(url string, body any, opts ...RequestOption) (*Response, error) {
	for _, opt := range opts {
		opt(request)
	}

	restyRequest := request.request

	resp, err := restyRequest.SetBody(body).Post(url)
	if err != nil {
		return nil, err
	}

	if resp.IsError() {
		return nil, errors.New(resp.Status())
	}

	return &Response{response: resp}, nil
}

func (request *Request) Delete(url string, opts ...RequestOption) (*Response, error) {
	for _, opt := range opts {
		opt(request)
	}

	restyRequest := request.request

	resp, err := restyRequest.Delete(url)
	if err != nil {
		return nil, err
	}

	if resp.IsError() {
		return nil, errors.New(resp.Status())
	}

	return &Response{response: resp}, nil
}

func (request *Request) Put(url string, body any, opts ...RequestOption) (*Response, error) {
	for _, opt := range opts {
		opt(request)
	}

	restyRequest := request.request

	resp, err := restyRequest.SetBody(body).Put(url)
	if err != nil {
		return nil, err
	}

	if resp.IsError() {
		return nil, errors.New(resp.Status())
	}

	return &Response{response: resp}, nil
}

func (request *Request) Get(url string, opts ...RequestOption) (*Response, error) {
	for _, opt := range opts {
		opt(request)
	}

	restyRequest := request.request

	resp, err := restyRequest.Get(url)
	if err != nil {
		return nil, err
	}

	if resp.IsError() {
		return nil, errors.New(resp.Status())
	}

	return &Response{response: resp}, nil
}

type Response struct {
	response *resty.Response
}

func (response *Response) Body() []byte {
	return response.response.Body()
}

func (response *Response) Json(resp any) error {
	body := response.response.Body()
	if body == nil || len(body) == 0 {
		return errors.New("响应体为空")
	}

	return json.Unmarshal(body, resp)
}