package adapterimpl

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"

	"github.com/go-resty/resty/v2"

	"github.com/tidbcloud/tidb-management-service/serverless-global-service/domain/adapter"
	"github.com/tidbcloud/tidb-management-service/serverless-global-service/pkg/config"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
	"github.com/tidbcloud/tidb-management-service/utilities/httputil"
)

type fakeBilling struct{}

func NewFakeBilling() adapter.BillingService {
	return &fakeBilling{}
}

func (f *fakeBilling) GetPlans(ctx context.Context, id uint64) (*adapter.TenantPlanInfo, error) {
	return &adapter.TenantPlanInfo{
		EffectivePlan: "on_demand",
	}, nil
}

func (f fakeBilling) CanCreateScalableCluster(
	ctx context.Context,
	region, provider, resourceType string,
	tenantId uint64,
) (*adapter.ActionCheckResp, error) {
	return &adapter.ActionCheckResp{
		Pass:   true,
		Reason: "",
	}, nil
}

type billing struct {
	client      *resty.Client
	billingAddr string
}

func NewBilling() adapter.BillingService {
	return &billing{
		client:      resty.New(),
		billingAddr: config.Global().ServerConfig.DependentSvcAddresses.Billing2,
	}
}

func (f *billing) callBilling(
	ctx context.Context,
	method, apiPath string,
	pathParams map[string]string,
	body, output any,
) bizerr.BizError {
	baseUrl := fmt.Sprintf("http://%s", f.billingAddr)

	resp, err := httputil.DoRequest(ctx, f.client, method, baseUrl, apiPath, func(req *resty.Request) {
		req.SetPathParams(pathParams)
		req.ForceContentType("application/json")
		if body != nil {
			req.SetBody(body)
		}
	})
	if err != nil {
		return bizerr.New(bizerr.ErrCodeSvcBilling, "callBilling failed: %s", err.Error())
	}
	if resp.StatusCode() != http.StatusOK {
		return bizerr.New(bizerr.ErrCodeSvcBilling, " http status code != 200")
	}
	if output != nil {
		if err := json.Unmarshal(resp.Body(), output); err != nil {
			return bizerr.New(bizerr.ErrCodeSvcBilling, "unmarshal response failed: %s", err)
		}
	}

	return nil
}

func (f *billing) GetPlans(ctx context.Context, id uint64) (*adapter.TenantPlanInfo, error) {
	const (
		path = "/api/v1/billing/tenants/{tenant_id}/plans"
	)

	plan := &adapter.TenantPlanInfo{}
	if err := f.callBilling(ctx, resty.MethodGet, path, map[string]string{"tenant_id": fmt.Sprint(id)}, nil, plan); err != nil {
		return nil, err.Chain("Billing2.GetPlans")
	}
	return plan, nil
}

func (f *billing) CanCreateScalableCluster(
	ctx context.Context,
	region, provider, resourceType string,
	tenantId uint64,
) (*adapter.ActionCheckResp, error) {
	const (
		path = "/api/v1/billing/tenants/{tenant_id}/billing_action_check"
	)
	if resourceType == "" {
		resourceType = adapter.BillingResourceTypeServerlessCluster
	}

	actionCheck := &adapter.ActionCheckReq{
		Resource: adapter.Resource{
			Type:       resourceType,
			InstanceID: "",
			Region:     region,
			Provider:   provider,
			IsFree:     false,
		},
		Action: "add",
	}

	resp := &adapter.ActionCheckResp{}

	if err := f.callBilling(ctx, resty.MethodPost, path, map[string]string{"tenant_id": fmt.Sprint(tenantId)}, actionCheck, resp); err != nil {
		return nil, err.Chain("Billing2.ActionCheck")
	}

	return resp, nil
}
