package repo

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

	"github.com/ajg/form"
	"github.com/andygrunwald/go-jira"
)

// nolint:gosec
const (
	ListO11yClusterFormat = "https://www.gs.us-west-2.aws.observability.tidbcloud.com/api/v1/clusters/?vendor=%s&region=%s&bizType=%s"
	GetSecretMetricFormat = "https://www.ds.%s.observability.tidbcloud.com/internal/metrics/d5d1a915-1d37-22a7-82b8-8cb67cc57820/api/v1/query?query=sum(certmanager_certificate_expiration_timestamp_seconds{exported_namespace=%%22tidb%s%%22})%%20by(name)"
)

type Auth0Req struct {
	GrantType    string `yaml:"grantType" json:"grant_type" form:"grant_type"`
	ClientID     string `yaml:"clientID" json:"client_id" form:"client_id"`
	ClientSecret string `yaml:"clientSecret" json:"client_secret" form:"client_secret"`
	Audience     string `yaml:"audience" json:"audience" form:"audience"`
}
type Auth0Resp struct {
	AccessToken string `json:"access_token" form:"access_token"`
}

func PrepareToken(req Auth0Req) string {
	body, err := form.EncodeToString(req)
	if err != nil {
		return ""
	}
	resp, err := http.Post("https://tidb-soc2.us.auth0.com/oauth/token", "application/x-www-form-urlencoded", strings.NewReader(body))
	if err != nil {
		return ""
	}
	respContent, err := io.ReadAll(resp.Body)
	if err != nil {
		return ""
	}
	auth0Resp := &Auth0Resp{}
	err = json.Unmarshal(respContent, auth0Resp)
	if err != nil {
		return ""
	}
	return auth0Resp.AccessToken
}

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

func WithHeader(request *http.Request, token string) {
	request.Header.Set("Content-Type", "application/json")
	request.Header.Set("Authorization", fmt.Sprintf("%s %s", "Bearer", token))
}

type QueryClustersResp struct {
	Clusters []ClusterBaseInfo `json:"clusters"`
	//ContinueResponse
}
type ContinueResponse struct {
	ContinueToken      string `json:"continueToken" form:"continueToken"`
	RemainingItemCount int64  `json:"remainingItemCount" form:"remainingItemCount"`
}

func UnmarshalRespData(data []byte, v interface{}) error {
	type Result struct {
		Code int         `json:"code"`
		Data interface{} `json:"data"`
	}
	result := &Result{
		Data: v,
	}
	return json.Unmarshal(data, result)
}

type ClusterBaseInfo struct {
	Vendor             string   `json:"vendor" form:"vendor" validate:"required,max=16"`
	Region             string   `json:"region" form:"region" validate:"required,max=16"`
	TenantID           string   `json:"tenantID" form:"tenantID" validate:"required,max=20"`
	ProjectID          string   `json:"projectID" form:"projectID" validate:"required,max=20"`
	ApplicationID      string   `json:"applicationID" form:"applicationID" validate:"required,max=20"`
	BizType            string   `json:"bizType" form:"bizType" validate:"required,max=32"`
	ID                 string   `json:"id" form:"id" validate:"max=64"`
	Tags               []string `json:"tags" form:"tags"`
	Version            string   `json:"version" form:"version"`
	ShareMode          string   `json:"shareMode" form:"shareMode" validate:"required,oneof=exclusive shared virtual enhanced"`
	Note               string   `json:"note" form:"note"`
	DataPropertyRights int      `json:"dataPropertyRights" form:"dataPropertyRights" validate:"oneof=0 1"`
}

func QueryCluster(vendor string, region string, bizType string, auth0Req Auth0Req) (*QueryClustersResp, error) {
	token := PrepareToken(auth0Req)

	query := fmt.Sprintf(ListO11yClusterFormat, vendor, region, bizType)
	request, _ := http.NewRequest("GET", query, nil)
	WithHeader(request, token)
	resp, err := http.DefaultClient.Do(request)
	if err != nil {
		return nil, fmt.Errorf("do request failed: %w", err)
	}
	defer resp.Body.Close()

	clustersResp := &QueryClustersResp{}
	respContent, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	err = UnmarshalRespData(respContent, clustersResp)
	return clustersResp, err
}

func GetSecret(region string, clusterID string) (*CertificateResp, error) {
	url := fmt.Sprintf(GetSecretMetricFormat, region, clusterID)
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("do request failed: %w", err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	cr := &CertificateResp{}
	err = json.Unmarshal(body, cr)
	if err != nil {
		return nil, err
	}

	return cr, nil
}

type CertificateResp struct {
	Status    string `json:"status"`
	IsPartial bool   `json:"isPartial"`
	Data      Data   `json:"data"`
}

type Data struct {
	ResultType string   `json:"resultType"`
	Result     []Result `json:"result"`
}

type Result struct {
	Metric Metric `json:"metric"`
	Value  []any  `json:"value"`
}

type Metric struct {
	Condition         string `json:"condition"`
	ExportedNamespace string `json:"exported_namespace"`
	Name              string `json:"name"`
}

func PostToJira(project string, des string, title string, priority string, component string, jiraClient *jira.Client) (*http.Response, error) {
	newIssue := &jira.Issue{
		Fields: &jira.IssueFields{
			Project: jira.Project{
				Key: project,
			},
			Type: jira.IssueType{
				Name: "Task",
			},
			Description: des,
			Summary:     title,
			Priority: &jira.Priority{
				Name: priority,
			},
			Components: []*jira.Component{
				{
					Name: component,
				},
			},
		},
	}
	_, resp, err := jiraClient.Issue.Create(newIssue)
	if err != nil {
		return nil, fmt.Errorf("failed to create issue: %w", err)
	}
	return resp.Response, nil
}
