package infrainfo

import (
	"context"
	"fmt"
	"strconv"

	"github.com/go-logr/logr"
	"go.uber.org/dig"

	"github.com/tidbcloud/devops/entity/k8supgrade"
	"github.com/tidbcloud/devops/pkg/infra/clients/coresvccli"
	"github.com/tidbcloud/devops/pkg/infra/clients/googlecli"
	"github.com/tidbcloud/devops/pkg/infra/clients/mgmthttpcli"
	"github.com/tidbcloud/devops/pkg/infra/config"
	pkgsvc "github.com/tidbcloud/devops/pkg/service"
	tenantsvc "github.com/tidbcloud/devops/pkg/service/tenant"
)

const svcName = "K8sInformationService"

type service struct {
	*pkgsvc.BaseService

	ThreeLayerArch ServiceAPI
	TenantSvc      tenantsvc.TenantServiceAPI
	CoreSvcCli     *coresvccli.CoreSvcCli
	Config         config.K8sUpgradeServiceConfig
}

var _ ServiceAPI = &service{}

type ServiceParam struct {
	dig.In

	Conf       *config.RootConfig
	Logger     logr.Logger
	GCPCli     googlecli.GoogleClient
	TenantSvc  tenantsvc.TenantServiceAPI
	CoreSvcCli *coresvccli.CoreSvcCli
	MgmtHTTP   mgmthttpcli.MgmtHTTPClient
}

type ServiceResult struct {
	dig.Out
	API     ServiceAPI
	Service pkgsvc.Service `group:"service"`
}

func NewService(p ServiceParam) (sr ServiceResult, err error) {
	base := pkgsvc.NewBaseService(svcName, pkgsvc.WithDummy(p.Conf.Services.K8sUpgrade.Dummy))
	if p.Conf.Services.K8sUpgrade.Dummy {
		sr.API = &DummyServiceAPI{}
		sr.Service = base
		return sr, nil
	}

	svc := &service{
		BaseService: base,
		Config:      p.Conf.Services.K8sUpgrade,
		TenantSvc:   p.TenantSvc,
		CoreSvcCli:  p.CoreSvcCli,
		ThreeLayerArch: &threeLayerArchSvc{
			mgmtHTTP:   p.MgmtHTTP,
			coreSvcCli: p.CoreSvcCli,
			logger:     p.Logger.WithName(svcName).WithName("ThreeLayerArchSvc"),
		},
	}

	sr.Service = svc
	sr.API = svc
	return sr, nil
}

func (svc *service) ListK8SInformation(ctx context.Context, req *ListK8SInformationReq) (list k8supgrade.K8sInfoList, err error) {
	list, err = svc.ThreeLayerArch.ListK8SInformation(ctx, req)
	if err != nil {
		svc.Logger(ctx).Error(err, "failed to list k8s information from the 3-layer architecture")
		return list, err
	}
	return list, nil
}

func (svc *service) GetK8sInformation(ctx context.Context, req *GetK8SInformationReq) (*k8supgrade.K8sInfo, error) {
	logger := svc.Logger(ctx).WithValues("name", req.Name, "cloudProvider", req.CloudProvider, "region", req.Region)

	info, err := svc.ThreeLayerArch.GetK8sInformation(ctx, req)
	if err != nil {
		logger.Error(err, "failed to get k8s information from the 3-layer architecture")
		return nil, err
	}

	svc.getTenantInfo(ctx, info)
	svc.getTiDBClusterName(ctx, info)
	return info, nil
}

func (svc *service) GetUpgradeOperation(ctx context.Context, req *CommonUpgradeOperationReq) (*k8supgrade.UpgradeOperation, error) {
	if is3LayerNamespace(req.Namespace) {
		return svc.ThreeLayerArch.GetUpgradeOperation(ctx, req)
	}

	return nil, fmt.Errorf("failed to get upgrade operation: %s/%s", req.Namespace, req.Name)
}

func (svc *service) PauseUpgradeOperation(ctx context.Context, req *CommonUpgradeOperationReq) error {
	if is3LayerNamespace(req.Namespace) {
		return svc.ThreeLayerArch.PauseUpgradeOperation(ctx, req)
	}

	return fmt.Errorf("failed to pause upgrade operation: %s/%s", req.Namespace, req.Name)
}

func (svc *service) DeleteUpgradeOperation(ctx context.Context, req *CommonUpgradeOperationReq) error {
	if is3LayerNamespace(req.Namespace) {
		return svc.ThreeLayerArch.DeleteUpgradeOperation(ctx, req)
	}

	return fmt.Errorf("failed to delete upgrade operation: %s/%s", req.Namespace, req.Name)
}

func (svc *service) CreateOrUpdateUpgradeOperation(ctx context.Context, req *CommonUpgradeOperationReq) error {
	if is3LayerNamespace(req.UpgradeOperation.Namespace) {
		return svc.ThreeLayerArch.CreateOrUpdateUpgradeOperation(ctx, req)
	}

	return fmt.Errorf("failed to create or update upgrade operation: %s/%s", req.UpgradeOperation.Namespace, req.UpgradeOperation.Name)
}

func (svc *service) PatchUpgradeOperationAnnotations(ctx context.Context, req *PatchUpgradeOperationAnnotationsReq) error {
	if is3LayerNamespace(req.Namespace) {
		return svc.ThreeLayerArch.PatchUpgradeOperationAnnotations(ctx, req)
	}

	return fmt.Errorf("failed to patch upgrade operation annotations: %s/%s", req.Namespace, req.Name)
}

// getTenantInfo gets the tenant information from the tenant service and sets it to the K8sInfo.
func (svc *service) getTenantInfo(ctx context.Context, info *k8supgrade.K8sInfo) {
	tid, err := strconv.ParseUint(info.TenantID, 10, 64)
	if err != nil {
		svc.Logger(ctx).Error(err, "failed to parse tenant ID", "tenantID", info.TenantID)
		return
	}

	tenant, err := svc.TenantSvc.GetTenant(ctx, tid)
	if err != nil {
		svc.Logger(ctx).Error(err, "failed to get tenant", "tenantID", tid)
		return
	}

	info.TenantName, info.TenantKind = tenant.Name, string(tenant.Kind)
	info.BusinessRegion = string(tenant.GetTenantRegion())
}

// getTiDBClusterName gets the name of tidb clusters and sets it to the K8sInfo.
func (svc *service) getTiDBClusterName(ctx context.Context, info *k8supgrade.K8sInfo) {
	for _, cluster := range info.TiDBClusters {
		cid, err := strconv.ParseUint(cluster.ID, 10, 64)
		if err != nil {
			svc.Logger(ctx).Error(err, "failed to parse cluster ID", "clusterID", cluster.ID)
			continue
		}
		c, err := svc.CoreSvcCli.GetCluster(ctx, cid, false)
		if err != nil {
			svc.Logger(ctx).Error(err, "failed to get cluster", "clusterID", cid)
			continue
		}
		cluster.Name = c.Name
	}
}
