package cloudconfig

import (
	"context"
	"fmt"

	ccent "github.com/tidbcloud/devops/entity/cloudconfig"
	"github.com/tidbcloud/devops/pkg/infra/utils/pointer"
	"github.com/tidbcloud/devops/pkg/service/cloudconfig/api"
	"github.com/tidbcloud/devops/pkg/service/cloudconfig/change/common"
	"github.com/tidbcloud/devops/pkg/service/cloudconfig/cvt"

	ccproto "github.com/tidbcloud/config-center/proto/config_center"
)

func (s *svc) ListTenantInitConfigurations(ctx context.Context, param api.ListTenantInitConfigurationsParam) ([]*ccent.Configuration, error) {
	return nil, fmt.Errorf("not implemented")
}

// GetTenantInitConfiguration return a tenant-init configuration.
//
// * `Version` is the default version.
// * `Components` is nil.
// * `Files` is data of tenant-init configuration.
func (s *svc) GetTenantInitConfiguration(ctx context.Context, loc ccent.TenantInitConfigurationLocator) (*ccent.Configuration, error) {
	defaultVer, err := s.cfgCenter.ConfigAPI.GetDefaultVersion(ctx, &ccproto.GetDefaultVersionRequest{
		ClusterType: cvt.To.ConfigCenter.ClusterType(loc.ClusterType),
		TenantId:    pointer.Pointer(loc.TenantID),
	})
	if err != nil {
		return nil, fmt.Errorf("get default version failed: %w", err)
	}

	// NG related: components
	componentsMap := make(map[string]*ccent.Component)
	if loc.ClusterType == ccent.ClusterTypeSharedPool {
		components := ccproto.ClusterType(ccproto.ClusterType_value[string(loc.ClusterType)]).AvailableComponents()
		for _, comp := range components {
			var compVer *ccproto.DefaultVersion
			if comp != ccproto.SHARED_POOL_COMPONENT_TIPROXY {
				compVer = defaultVer
			} else {
				compVer, err = s.cfgCenter.ConfigAPI.GetDefaultVersion(ctx, &ccproto.GetDefaultVersionRequest{
					ClusterType: cvt.To.ConfigCenter.ClusterType(loc.ClusterType),
					Component:   ccproto.SHARED_POOL_COMPONENT_TIPROXY,
					TenantId:    pointer.Pointer(loc.TenantID),
				})
				if err != nil {
					return nil, fmt.Errorf("get default version for component %s failed: %w", comp, err)
				}
			}
			componentImage, err := s.cfgCenter.ConfigAPI.GetComponentImage(ctx, &ccproto.GetComponentImageRequest{
				ClusterType:      cvt.To.ConfigCenter.ClusterType(loc.ClusterType),
				ComponentName:    comp,
				ComponentVersion: compVer.Version,
				TenantId:         &loc.TenantID,
			})
			if err != nil {
				return nil, fmt.Errorf("get tenant init component image for %s failed: %w", comp, err)
			}
			componentsMap[comp] = &ccent.Component{
				Name:              comp,
				BaseImage:         componentImage.BaseImage,
				EffectiveImageTag: componentImage.Tag,
			}
		}
	}

	req := &ccproto.ListTenantInitialCustomParamsRequest{
		ClusterType: cvt.To.ConfigCenter.ClusterType(loc.ClusterType),
		TenantId:    loc.TenantID,
	}
	resp, err := s.cfgCenter.ConfigAPI.ListTenantInitialCustomParams(ctx, req)
	if err != nil {
		return nil, fmt.Errorf("get tenant init config from config center failed: %w", err)
	}

	result := &ccent.Configuration{
		TargetTenantInit: &loc,
		Version:          defaultVer.Version,
		Components:       componentsMap,
		Files:            map[string]*ccent.FileConfiguration{},
	}

	for _, tenantInitRaw := range resp.TenantInitialCustomParams {
		configType := tenantInitRaw.ConfigurationType
		initParams := tenantInitRaw.InitialParams

		builder := common.NewUnifiedParameterBuilder()

		// add parameters as custom configs
		for key, param := range initParams {
			builder.AddCustom(cvt.From.ConfigCenter.NamedParameter(key, param))
		}

		// add attr to parameters
		err = builder.InjectAttrs(ctx, cvt.To.ConfigCenter.ClusterType(loc.ClusterType), configType, defaultVer.Version, s.cfgCenter)
		if err != nil {
			return nil, err
		}

		result.Files[configType] = &ccent.FileConfiguration{ // other fields are unsupported
			Filename:          configType, // config type is same as filename for tenant-init
			ConfigType:        configType,
			BelongToComponent: "",
			Parameters:        builder.Build(),
		}
	}
	return result, nil
}

func (s *svc) GetTenantInitVersions(ctx context.Context, loc ccent.TenantInitConfigurationLocator) (*ccent.TenantVersions, error) {
	// FIXME: do not hardcode
	if loc.ClusterType == ccent.ClusterTypeLogicalCluster || loc.ClusterType == ccent.ClusterTypeSharedPool {
		return &ccent.TenantVersions{}, nil
	}
	result := &ccent.TenantVersions{
		TiDB:    ccent.VersionFollowGlobal,
		TiProxy: ccent.VersionFollowGlobal,
		DM:      ccent.VersionFollowGlobal,
	}

	defaultResp, err := s.cfgCenter.ConfigAPI.GetDefaultVersion(ctx, &ccproto.GetDefaultVersionRequest{
		ClusterType: cvt.To.ConfigCenter.ClusterType(loc.ClusterType),
		Component:   ccproto.DEDICATED_TIDB_COMPONENT_TIDB,
		TenantId:    pointer.Pointer(loc.TenantID),
	})
	if err != nil {
		return nil, fmt.Errorf("get default tidb version failed: %w", err)
	}
	if defaultResp.Source == "tenant" {
		result.TiDB = defaultResp.Version
	}

	defaultResp, err = s.cfgCenter.ConfigAPI.GetDefaultVersion(ctx, &ccproto.GetDefaultVersionRequest{
		ClusterType: cvt.To.ConfigCenter.ClusterType(loc.ClusterType),
		Component:   ccproto.DEDICATED_TIDB_COMPONENT_TIPROXY,
		TenantId:    pointer.Pointer(loc.TenantID),
	})
	if err != nil {
		return nil, fmt.Errorf("get default tiproxy version failed: %w", err)
	}
	if defaultResp.Source == "tenant" {
		result.TiProxy = defaultResp.Version
	}

	defaultResp, err = s.cfgCenter.ConfigAPI.GetDefaultVersion(ctx, &ccproto.GetDefaultVersionRequest{
		ClusterType: cvt.To.ConfigCenter.ClusterType(loc.ClusterType),
		Component:   ccproto.DEDICATED_TIDB_COMPONENT_DM,
		TenantId:    pointer.Pointer(loc.TenantID),
	})
	if err != nil {
		return nil, fmt.Errorf("get default dm version failed: %w", err)
	}
	if defaultResp.Source == "tenant" {
		result.DM = defaultResp.Version
	}

	return result, nil
}

func (s *svc) ListTenantInitImages(ctx context.Context, loc ccent.TenantInitConfigurationLocator) (map[string]*ccent.Component, error) {
	components := ccproto.ClusterType(ccproto.ClusterType_value[string(loc.ClusterType)]).AvailableComponents()
	tenantDefaultVersion, err := s.cfgCenter.ConfigAPI.GetDefaultVersion(ctx, &ccproto.GetDefaultVersionRequest{
		ClusterType: cvt.To.ConfigCenter.ClusterType(loc.ClusterType),
		TenantId:    pointer.Pointer(loc.TenantID),
	})
	if err != nil {
		return nil, fmt.Errorf("get tenant default version failed: %w", err)
	}
	res := make(map[string]*ccent.Component)
	for _, comp := range components {
		compInfo, err := s.cfgCenter.ConfigAPI.GetComponentImage(ctx, &ccproto.GetComponentImageRequest{
			ClusterType:      cvt.To.ConfigCenter.ClusterType(loc.ClusterType),
			ComponentName:    comp,
			ComponentVersion: tenantDefaultVersion.Version,
			TenantId:         &loc.TenantID,
		})
		if err != nil {
			return nil, fmt.Errorf("get tenant init component image for %s failed: %w", comp, err)
		}
		res[comp] = &ccent.Component{
			Name:              comp,
			BaseImage:         compInfo.BaseImage,
			EffectiveImageTag: compInfo.Tag,
		}
	}
	return res, nil
}
