package global_client

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/go-resty/resty/v2"
	"github.com/juju/errors"
	"github.com/tidbcloud/auto-deploy/internal/utils"
)

func GetGlobalInnerDomain(instance string) string {
	return fmt.Sprintf("https://inner-central.%s.tidbcloud.com", instance)
}

type GlobalClient struct {
	instance string
	client   *resty.Client
}

func NewGlobalClient(instance, authKey string, timeout time.Duration, debug bool) *GlobalClient {
	baseUrl := GetGlobalInnerDomain(instance)
	client := &GlobalClient{
		instance: instance,
		client: resty.NewWithClient(&http.Client{
			Timeout:   timeout,
			Transport: http.DefaultTransport,
		}),
	}
	client.client.Debug = debug
	client.client.SetBaseURL(baseUrl)
	client.client.SetAuthScheme("Basic")
	client.client.SetAuthToken(authKey)
	utils.MaskSensitiveGithubOutput(authKey)
	return client
}

func (c *GlobalClient) GetResult(url string, query map[string]string, result any) error {
	resp, err := c.client.NewRequest().SetResult(&result).SetQueryParams(query).Get(url)
	return ParseErrorResponse(resp, err)
}

func (c *GlobalClient) Get(url string, query map[string]string) (string, int, error) {
	resp, err := c.client.NewRequest().SetQueryParams(query).Get(url)
	if err != nil {
		return "", -1, ParseErrorResponse(resp, err)
	}
	return string(resp.Body()), resp.StatusCode(), nil
}

func (c *GlobalClient) PostResult(url string, query map[string]string, body, result any) error {
	resp, err := c.client.NewRequest().SetHeader("Content-Type", "application/json").
		SetResult(&result).SetQueryParams(query).SetBody(body).Post(url)
	return ParseErrorResponse(resp, err)
}

func (c *GlobalClient) Post(url string, query map[string]string, body any) (string, int, error) {
	resp, err := c.client.NewRequest().SetHeader("Content-Type", "application/json").
		SetQueryParams(query).SetBody(body).Post(url)
	if err != nil {
		return "", -1, ParseErrorResponse(resp, err)
	}
	return string(resp.Body()), resp.StatusCode(), nil
}

func (c *GlobalClient) PutResult(url string, query map[string]string, body, result any) error {
	resp, err := c.client.NewRequest().SetHeader("Content-Type", "application/json").
		SetResult(&result).SetQueryParams(query).SetBody(body).Put(url)
	return ParseErrorResponse(resp, err)
}

func (c *GlobalClient) Put(url string, query map[string]string, body any) (string, int, error) {
	resp, err := c.client.NewRequest().SetHeader("Content-Type", "application/json").
		SetQueryParams(query).SetBody(body).Put(url)
	if err != nil {
		return "", -1, ParseErrorResponse(resp, err)
	}
	return string(resp.Body()), resp.StatusCode(), nil
}

func (c *GlobalClient) PatchResult(url string, query map[string]string, body, result any) error {
	resp, err := c.client.NewRequest().SetHeader("Content-Type", "application/json").
		SetResult(&result).SetQueryParams(query).SetBody(body).Patch(url)
	return ParseErrorResponse(resp, err)
}

func (c *GlobalClient) Patch(url string, query map[string]string, body any) (string, int, error) {
	resp, err := c.client.NewRequest().SetHeader("Content-Type", "application/json").
		SetQueryParams(query).SetBody(body).Patch(url)
	if err != nil {
		return "", -1, ParseErrorResponse(resp, err)
	}
	return string(resp.Body()), resp.StatusCode(), nil
}

func (c *GlobalClient) Delete(url string, query map[string]string) (string, int, error) {
	resp, err := c.client.NewRequest().SetQueryParams(query).Delete(url)
	if err != nil {
		return "", -1, ParseErrorResponse(resp, err)
	}
	return string(resp.Body()), resp.StatusCode(), nil
}

func ParseErrorResponse(resp *resty.Response, err error) error {
	if err != nil {
		return err
	}
	if resp.StatusCode() != http.StatusOK {
		var errResp error = &ErrorResponse{}
		if strings.HasPrefix(resp.Request.RawRequest.URL.Path, "/inner/billing/") {
			errResp = &BillingErrorResponse{}
		}
		err = json.Unmarshal(resp.Body(), errResp)
		if err != nil {
			return errors.Annotatef(err, "Failed to parse error response")
		} else {
			return errResp
		}
	}
	return nil
}

func (c *GlobalClient) NewRequest() *resty.Request {
	return c.client.NewRequest()
}
