package checker

import (
	"context"
	"fmt"
	"strconv"
	"strings"
	"time"
	"unicode"

	"github.com/andygrunwald/go-jira"

	"github.com/tidbcloud/devops/pkg/infra/log"
	"github.com/tidbcloud/devops/pkg/service/monitoring/repo"
)

type CertChecker struct {
	env      string
	regions  []string
	jiraCli  *jira.Client
	auth0Req repo.Auth0Req
}

func NewCertificateChecker(
	env string,
	regions []string,
	auth0Req repo.Auth0Req,
	jiraCli *jira.Client,
) (*CertChecker, error) {

	return &CertChecker{
		env:      env,
		regions:  regions,
		jiraCli:  jiraCli,
		auth0Req: auth0Req,
	}, nil
}

func (c *CertChecker) Check(ctx context.Context) error {
	logger := log.FromContext(ctx)

	logger.Info("start check cluster certificate")

	err := c.checkCertificate()
	if err != nil {
		return fmt.Errorf("check cluster certificate failed: %w", err)
	}

	logger.Info("check cluster certificate done")

	return nil
}

func (c *CertChecker) checkCertificate() error {
	expireCluster := make(map[string]map[string]int)
	clusterInfo := make(map[string]map[string]string)
	for _, r := range c.regions {
		region := strings.Split(r, ".")
		err := c.getClusterCertificate(region[0], region[1], expireCluster, clusterInfo)
		if err != nil {
			return fmt.Errorf("get cluster and certificate from o11y failed: %s", err)
		}
	}
	if len(expireCluster) > 0 {
		des := ""
		i := 1
		for k, v := range expireCluster {
			clusterRegion := fmt.Sprintf("%s %s", clusterInfo[k]["vendor"], clusterInfo[k]["region"])
			tenantID, _ := strconv.ParseUint(clusterInfo[k]["tenantID"], 10, 64)
			des += fmt.Sprintf("%v. cluster_id=%s, tenant_id=%v, region=%s\n", i, k, tenantID, clusterRegion)
			for kk, vv := range v {
				des += fmt.Sprintf("certificate=%s, days=%v\n", kk, vv)
			}
			i++
		}
		title := fmt.Sprintf("[%s]ClusterCertificateToBeExpire", c.env)

		resp, err := repo.PostToJira("O11Y", des, title, "Major", "opsportal", c.jiraCli)
		if err != nil {
			return fmt.Errorf("post issue to jira failed: %s", err)
		}
		if resp.StatusCode/100 != 2 {
			return fmt.Errorf("post issue to jira failed: %s", resp.Status)
		}
	}
	return nil
}

func (c *CertChecker) getClusterCertificate(region string, vendor string, mp map[string]map[string]int, clusterInfo map[string]map[string]string) error {
	clustersResp, err := repo.QueryCluster(vendor, region, "tidbcloud", c.auth0Req)
	if err != nil {
		return err
	}
	for _, cluster := range clustersResp.Clusters {
		// save the cluster info
		clusterID := cluster.ApplicationID
		if _, ok := clusterInfo[clusterID]; !ok {
			clusterInfo[clusterID] = make(map[string]string)
		}
		clusterInfo[clusterID]["tenantID"] = cluster.TenantID
		clusterInfo[clusterID]["region"] = cluster.Region
		clusterInfo[clusterID]["vendor"] = cluster.Vendor

		isDigit := unicode.IsDigit(rune(clusterID[0]))
		if !isDigit {
			continue
		}
		r := fmt.Sprintf("%s.%s", region, vendor)
		cert, err := repo.GetSecret(r, clusterID)
		if err != nil {
			return err
		}
		for _, value := range cert.Data.Result {
			_, days := toDate(value.Value[1].(string))
			if days <= 60 {
				if _, ok := mp[clusterID]; !ok {
					mp[clusterID] = make(map[string]int)
				}
				mp[clusterID][value.Metric.Name] = days
			}
		}
	}
	return nil
}

func toDate(t string) (time.Time, int) {
	timestamp, _ := strconv.ParseInt(t, 10, 64)
	timeObj := time.Unix(timestamp, 0)
	now := time.Now()

	duration := timeObj.Sub(now)

	days := int(duration.Hours() / 24)
	return timeObj, days
}
