package o11y

import (
	"fmt"
	"net/url"
	"strings"

	"github.com/ajg/form"
)

type BaseResult struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

func (ret *BaseResult) BizError() error {
	if ret.Code == 0 {
		return nil
	}
	return fmt.Errorf("got biz error, code: %d, message: %s", ret.Code, ret.Message)
}

//curl -X POST --url 'https://tidb-soc2.us.auth0.com/oauth/token' \
//--header 'content-type: application/x-www-form-urlencoded' \
//--data grant_type=client_credentials \
//--data client_id=$client_id \
//--data client_secret=$client_secret \
//--data audience=https://tidb-soc2.us.auth0.com/api/v2/

type FetchAuth0TokenParam struct {
	GrantType    string `json:"grant_type" form:"grant_type"`
	ClientID     string `json:"client_id" form:"client_id"`
	ClientSecret string `json:"client_secret" form:"client_secret"`
	Audience     string `json:"audience" form:"audience"`
}

func (param *FetchAuth0TokenParam) ToForm() (url.Values, error) {
	return form.EncodeToValues(param)
}

type FetchAuth0TokenData struct {
	AccessToken string `json:"access_token"`
	Scope       string `json:"scope"`
	ExpiresIn   int    `json:"expires_in"` // in seconds
	TokenType   string `json:"token_type"`
}

// curl -H 'Authorization: Bearer $TOEKN 'http://www.gs.us-west-2.aws.observability-staging.pingcap.cloud/api/v1/clusters/?region=us-west-2&vendor=aws
type ListClusterParam struct {
	BizType  string `json:"bizType" form:"bizType,omitempty"`
	Vendor   string `json:"vendor" form:"vendor"`
	Region   string `json:"region" form:"region"`
	TenantID string `json:"tenantID" form:"tenantID,omitempty"`
	// ProjectID is the O11Y GS project identifier. It must not be populated
	// with a mutable DBaaS project ID unless both domains share that identity.
	ProjectID     string `json:"projectID" form:"projectID,omitempty"`
	ApplicationID string `json:"applicationID" form:"applicationID,omitempty"`
	ContinueToken string `json:"continueToken" form:"continueToken,omitempty"`
	ShowDeleted   bool   `json:"showDeleted" form:"showDeleted,omitempty"`
}

func (param *ListClusterParam) ToForm() (url.Values, error) {
	return form.EncodeToValues(param)
}

func (param *ListClusterParam) isShared() bool {
	return strings.HasSuffix(param.BizType, "devtier")
}

type GetClusterParam struct {
	ID      string `json:"id" form:"id"`
	Region  string `json:"region" form:"region"`
	Vendor  string `json:"vendor" form:"vendor"`
	BizType string `json:"-" form:"-"`
}

func (param *GetClusterParam) isShared() bool {
	return strings.HasSuffix(param.BizType, "devtier")
}

type ListGlobalClusterParam struct {
	BizType       string `json:"bizType" form:"bizType,omitempty"`
	TenantID      string `json:"tenantID" form:"tenantID,omitempty"`
	ProjectID     string `json:"projectID" form:"projectID,omitempty"`
	ApplicationID string `json:"applicationID" form:"applicationID,omitempty"`
}

// ListGlobalClusterParam always use shared mode (use prod o11y server)
func (param *ListGlobalClusterParam) isShared() bool {
	return true
}

func (param *ListGlobalClusterParam) ToForm() (url.Values, error) {
	return form.EncodeToValues(param)
}

// we only care about basic info (struct.ClusterBaseInfo)
type ClusterInfo struct {
	Vendor        string   `json:"vendor"`
	Region        string   `json:"region"`
	TenantID      string   `json:"tenantID"`
	ProjectID     string   `json:"projectID"`
	ApplicationID string   `json:"applicationID"`
	BizType       string   `json:"bizType"`
	ID            string   `json:"id"`
	Tags          []string `json:"tags"`
	Version       string   `json:"version"`
	// ShareMode     string   `json:"shareMode"`
	// Note          string   `json:"note"`
	// DataPropertyRights unknown
	InternalReadUri     string        `json:"internalReadUri"`
	MultitenantQueryUri string        `json:"multitenantQueryUri"`
	LogQueryUri         string        `json:"logQueryUri"`
	StorageInfo         []StorageInfo `json:"storageInfo"`
}

type StorageInfo struct {
	BucketName string `json:"bucketName"`
	Category   string `json:"category"`
	ObjectPath string `json:"objectPath"`
	DataARN    string `json:"dataARN"`
}

type ObjectStorageLocation struct {
	BucketName string
	ObjectPath string
}

func (c *ClusterInfo) StorageLocation(category string) (*ObjectStorageLocation, bool) {
	if c == nil {
		return nil, false
	}
	for _, info := range c.StorageInfo {
		if !strings.EqualFold(info.Category, category) {
			continue
		}
		if info.BucketName == "" || info.ObjectPath == "" {
			return nil, false
		}
		return &ObjectStorageLocation{
			BucketName: info.BucketName,
			ObjectPath: info.ObjectPath,
		}, true
	}
	return nil, false
}

type ListClusterResult struct {
	BaseResult
	Data *ListClusterData `json:"data"`
}

type ListClusterData struct {
	Clusters           []ClusterInfo `json:"clusters"`
	ContinueToken      string        `json:"continueToken"`
	RemainingItemCount int           `json:"remainingItemCount"`
}

func (ret *ListClusterResult) BizError() error {
	if ret.Code == 0 {
		return nil
	}
	return fmt.Errorf("got biz error, code: %d, message: %s", ret.Code, ret.Message)
}

func (data *ListClusterData) HasMore() bool {
	return data.RemainingItemCount > 0
}

func (data *ListClusterData) GetID(applicationID string) (string, bool) {
	if len(data.Clusters) == 0 {
		return "", false
	}
	for _, c := range data.Clusters {
		if c.ApplicationID == applicationID {
			return c.ID, true
		}
	}
	return "", false
}

// only marshal field we care about
type GetClusterData struct {
	//AgentInfo struct {
	//	Configs []struct {
	//		Category string `json:"category"`
	//		Content  string `json:"content"`
	//	} `json:"configs"`
	//	RoleARN string `json:"roleARN"`
	//} `json:"agentInfo"`
	ApplicationID string `json:"applicationID"`
	BizType       string `json:"bizType"`
	ID            string `json:"id"`
	//MonitorInfo   struct {
	//	DNSName             string `json:"dnsName"`
	//	EndpointServiceName string `json:"endpointServiceName"`
	//	SpecID              string `json:"specID"`
	//} `json:"monitorInfo"`
	Note      string `json:"note"`
	ProjectID string `json:"projectID"`
	Region    string `json:"region"`
	ShareMode string `json:"shareMode"`
	Status    string `json:"status"` // CREATING|ACTIVE|UPDATING|DELETING|CREATION_FAILED|TO_BE_DELETED
	//StorageInfo []struct {
	//	BucketName string `json:"bucketName"`
	//	Category   string `json:"category"`
	//	DataARN    string `json:"dataARN"`
	//} `json:"storageInfo"`
	//Tags     []interface{} `json:"tags"`
	TenantID        string `json:"tenantID"`
	Vendor          string `json:"vendor"`
	Version         string `json:"version"`
	InternalReadUri string `json:"internalReadUri"`
	InternalLokiUri string `json:"internalLokiUri"`
}

func (data *GetClusterData) IsReady() bool {
	if data.ShareMode == "shared" {
		switch data.Status {
		case "ACTIVE", "UPDATING", "DELETING", "TO_BE_DELETED", "TO_BE_PAUSED", "PAUSED":
			return true
		}
	} else {
		switch data.Status {
		case "ACTIVE", "UPDATING", "DELETING", "TO_BE_DELETED", "TO_BE_PAUSED":
			return true
		}
	}
	return false
}

type GetClusterResult struct {
	BaseResult
	Data *GetClusterData `json:"data"`
}
