package region

import (
	"context"
	"errors"

	"github.com/tidbcloud/pkgv2/db"
	"go.opentelemetry.io/otel"
	"gorm.io/gorm"

	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/entity"
	regionrepo "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/region"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/infra/repoimpl/schema"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

var regionProviderIdentityRepoImplTracer = otel.Tracer("regionProviderIdentityRepoImpl")

func NewRegionProviderIdentityRepo(provider db.Provider) regionrepo.ProviderIdentityRepo {
	return &regionProviderIdentityRepoImpl{
		provider: provider,
	}
}

type regionProviderIdentityRepoImpl struct {
	provider db.Provider
}

func (r *regionProviderIdentityRepoImpl) Load(ctx context.Context,
	id uint64,
) (*entity.RegionProviderIdentity, bizerr.BizError) {
	ctx, span := regionProviderIdentityRepoImplTracer.Start(ctx, "regionProviderIdentityRepoImpl.Load")
	defer span.End()

	if id == 0 {
		return nil, bizerr.New(bizerr.ErrCodeParams, "id is required")
	}

	rdb := r.provider.ReadDB(ctx)
	var schemaProviderIdentity *schema.ProviderIdentity
	if err := rdb.Model(&schema.ProviderIdentity{}).
		Where("`provider_identities`.`id` = ?", id).
		First(&schemaProviderIdentity).Error; err != nil {
		return nil, bizerr.WrapGormError(err, "get provider identity by id %d", id)
	}

	return ConvertProviderIdentityModelToEntity(schemaProviderIdentity)
}

func (r *regionProviderIdentityRepoImpl) ListProviderIdentityByRegion(ctx context.Context,
	regionId uint64) ([]*entity.RegionProviderIdentity, bizerr.BizError) {
	ctx, span := regionProviderIdentityRepoImplTracer.Start(ctx, "regionProviderIdentityRepoImpl.ListProviderIdentityByRegion")
	defer span.End()

	if regionId == 0 {
		return nil, bizerr.New(bizerr.ErrCodeParams, "region id is required")
	}

	rdb := r.provider.ReadDB(ctx)
	var schemaProviderIdentities []*schema.ProviderIdentity
	if err := rdb.Model(&schema.ProviderIdentity{}).
		Where("`provider_identities`.`region_id` = ?",
			regionId).
		Order("created_at ASC").
		Find(&schemaProviderIdentities).Error; err != nil {
		return nil, bizerr.WrapGormError(err, "list provider identities by region failed")
	}

	var result []*entity.RegionProviderIdentity
	for _, schemaProviderIdentity := range schemaProviderIdentities {
		entityProviderIdentity, err := ConvertProviderIdentityModelToEntity(schemaProviderIdentity)
		if err != nil {
			return nil, err
		}
		result = append(result, entityProviderIdentity)
	}
	return result, nil
}

func (r *regionProviderIdentityRepoImpl) Save(ctx context.Context, identity *entity.RegionProviderIdentity) bizerr.BizError {
	ctx, span := regionProviderIdentityRepoImplTracer.Start(ctx, "regionProviderIdentityRepoImpl.Save")
	defer span.End()

	if identity == nil {
		return bizerr.New(bizerr.ErrCodeParams, "provider identity is required")
	}

	rdb := r.provider.WriteDB(ctx)
	schemaProviderIdentity, err := convertProviderIdentityEntityToModel(identity)
	if err != nil {
		return err
	}

	if err := rdb.Save(schemaProviderIdentity).Error; err != nil {
		return bizerr.WrapGormError(err, "save provider identity failed")
	}
	return nil
}

func (r *regionProviderIdentityRepoImpl) QueryDefaultProviderIdentity(ctx context.Context,
	providerId uint64, servicePlan string, regionId uint64, tenantId uint64) ([]*entity.DefaultProviderIdentity, bizerr.BizError) {
	ctx, span := regionProviderIdentityRepoImplTracer.Start(ctx, "regionProviderIdentityRepoImpl.QueryDefaultProviderIdentity")
	defer span.End()

	if providerId == 0 {
		return nil, bizerr.New(bizerr.ErrCodeParams, "provider id is required")
	}
	if servicePlan == "" {
		return nil, bizerr.New(bizerr.ErrCodeParams, "service plan is required")
	}

	rdb := r.provider.ReadDB(ctx)
	var schemaDefaultProviderIdentities []*schema.DefaultProviderIdentity

	// Query with all possible combinations
	// Priority 1: regionId + tenantId match
	// Priority 2: tenantId match, regionId = 0
	// Priority 3: regionId match, tenantId = 0
	// Priority 4: regionId = 0, tenantId = 0
	if err := rdb.Model(&schema.DefaultProviderIdentity{}).
		Where("provider_id = ? AND service_plan = ?", providerId, servicePlan).
		Where("(region_id = ? AND tenant_id = ?) OR (region_id = 0 AND tenant_id = ?) OR (region_id = ? AND tenant_id = 0) OR (region_id = 0 AND tenant_id = 0)",
			regionId, tenantId, tenantId, regionId).
		Find(&schemaDefaultProviderIdentities).Error; err != nil {
		return nil, bizerr.WrapGormError(err, "query default provider identities failed")
	}

	var result []*entity.DefaultProviderIdentity
	for _, schemaDefaultProviderIdentity := range schemaDefaultProviderIdentities {
		result = append(result, convertDefaultProviderIdentityModelToEntity(schemaDefaultProviderIdentity))
	}
	return result, nil
}

func (r *regionProviderIdentityRepoImpl) GetProviderIdentityByIdentity(ctx context.Context,
	regionId uint64, identity string) (*entity.RegionProviderIdentity, bizerr.BizError) {
	ctx, span := regionProviderIdentityRepoImplTracer.Start(ctx, "regionProviderIdentityRepoImpl.GetProviderIdentityByIdentity")
	defer span.End()

	if regionId == 0 {
		return nil, bizerr.New(bizerr.ErrCodeParams, "region id is required")
	}
	if identity == "" {
		return nil, bizerr.New(bizerr.ErrCodeParams, "identity is required")
	}

	rdb := r.provider.ReadDB(ctx)
	var schemaProviderIdentity *schema.ProviderIdentity
	if err := rdb.Model(&schema.ProviderIdentity{}).
		Where("`provider_identities`.`region_id` = ? AND `provider_identities`.`identity` = ?", regionId, identity).
		First(&schemaProviderIdentity).Error; err != nil {
		return nil, bizerr.WrapGormError(err, "get provider identity by identity failed")
	}

	return ConvertProviderIdentityModelToEntity(schemaProviderIdentity)
}

func (r *regionProviderIdentityRepoImpl) SaveDefaultProviderIdentity(ctx context.Context,
	defaultIdentity *entity.DefaultProviderIdentity) bizerr.BizError {
	ctx, span := regionProviderIdentityRepoImplTracer.Start(ctx, "regionProviderIdentityRepoImpl.SaveDefaultProviderIdentity")
	defer span.End()

	if defaultIdentity == nil {
		return bizerr.New(bizerr.ErrCodeParams, "default provider identity is required")
	}

	rdb := r.provider.WriteDB(ctx)
	schemaDefaultIdentity := convertDefaultProviderIdentityEntityToModel(defaultIdentity)

	err := rdb.Transaction(func(tx *gorm.DB) error {
		var existing schema.DefaultProviderIdentity
		err := tx.Unscoped().
			Where("provider_id = ? AND service_plan = ? AND region_id = ? AND tenant_id = ?",
				defaultIdentity.ProviderId, defaultIdentity.ServicePlan, defaultIdentity.RegionId, defaultIdentity.TenantId).
			First(&existing).Error
		if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
			return err
		}

		if errors.Is(err, gorm.ErrRecordNotFound) {
			return tx.Create(schemaDefaultIdentity).Error
		}

		schemaDefaultIdentity.ID = existing.ID
		schemaDefaultIdentity.CreatedAt = existing.CreatedAt
		// Restore soft-deleted record when the same unique key is re-saved.
		schemaDefaultIdentity.DeletedAt = gorm.DeletedAt{}

		return tx.Unscoped().Model(&existing).
			Select("identity", "updated_at", "deleted_at").
			Updates(schemaDefaultIdentity).Error
	})
	if err != nil {
		return bizerr.WrapGormError(err, "save default provider identity failed")
	}
	return nil
}

func (r *regionProviderIdentityRepoImpl) GetDefaultProviderIdentityByRegionAndTenant(
	ctx context.Context,
	providerId uint64, servicePlan string,
	regionId uint64, tenantId uint64,
) (*entity.DefaultProviderIdentity, bizerr.BizError) {
	ctx, span := regionProviderIdentityRepoImplTracer.Start(ctx, "regionProviderIdentityRepoImpl.GetDefaultProviderIdentityByRegionAndTenant")
	defer span.End()

	if providerId == 0 {
		return nil, bizerr.New(bizerr.ErrCodeParams, "provider id is required")
	}
	if servicePlan == "" {
		return nil, bizerr.New(bizerr.ErrCodeParams, "service plan is required")
	}
	if regionId == 0 {
		return nil, bizerr.New(bizerr.ErrCodeParams, "region id is required")
	}

	rdb := r.provider.ReadDB(ctx)
	var mo schema.DefaultProviderIdentity
	if err := rdb.Model(&schema.DefaultProviderIdentity{}).
		Where("provider_id = ? AND service_plan = ? AND region_id = ? AND tenant_id = ?",
			providerId, servicePlan, regionId, tenantId).
		First(&mo).Error; err != nil {
		return nil, bizerr.WrapGormError(err, "get default provider identity failed")
	}

	return convertDefaultProviderIdentityModelToEntity(&mo), nil
}

func (r *regionProviderIdentityRepoImpl) ListDefaultProviderIdentitiesByRegion(
	ctx context.Context,
	providerId uint64,
	regionId uint64,
) ([]*entity.DefaultProviderIdentity, bizerr.BizError) {
	ctx, span := regionProviderIdentityRepoImplTracer.Start(ctx, "regionProviderIdentityRepoImpl.ListDefaultProviderIdentitiesByRegion")
	defer span.End()

	if providerId == 0 {
		return nil, bizerr.New(bizerr.ErrCodeParams, "provider id is required")
	}
	if regionId == 0 {
		return nil, bizerr.New(bizerr.ErrCodeParams, "region id is required")
	}

	rdb := r.provider.ReadDB(ctx)
	var mos []*schema.DefaultProviderIdentity
	if err := rdb.Model(&schema.DefaultProviderIdentity{}).
		Where("provider_id = ? AND region_id = ?", providerId, regionId).
		Order("tenant_id ASC").
		Find(&mos).Error; err != nil {
		return nil, bizerr.WrapGormError(err, "list default provider identities by region failed")
	}

	result := make([]*entity.DefaultProviderIdentity, 0, len(mos))
	for _, mo := range mos {
		result = append(result, convertDefaultProviderIdentityModelToEntity(mo))
	}
	return result, nil
}

func (r *regionProviderIdentityRepoImpl) DeleteDefaultProviderIdentityByRegionAndTenant(
	ctx context.Context,
	providerId uint64,
	regionId uint64, tenantId uint64,
) bizerr.BizError {
	ctx, span := regionProviderIdentityRepoImplTracer.Start(ctx, "regionProviderIdentityRepoImpl.DeleteDefaultProviderIdentityByRegionAndTenant")
	defer span.End()

	if providerId == 0 {
		return bizerr.New(bizerr.ErrCodeParams, "provider id is required")
	}
	if regionId == 0 {
		return bizerr.New(bizerr.ErrCodeParams, "region id is required")
	}

	rdb := r.provider.WriteDB(ctx)
	tx := rdb.Where("provider_id = ? AND region_id = ? AND tenant_id = ?",
		providerId, regionId, tenantId).
		Delete(&schema.DefaultProviderIdentity{})
	if err := tx.Error; err != nil {
		return bizerr.WrapGormError(err, "delete default provider identity failed")
	}

	if tx.RowsAffected == 0 {
		return bizerr.WrapGormError(gorm.ErrRecordNotFound, "default provider identity not found")
	}

	return nil
}
