package schema

import (
	"context"

	"github.com/samber/lo"
	"gorm.io/gorm"

	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

type Provider struct {
	gorm.Model

	Name        string `gorm:"index:,unique;type:ENUM('aws', 'gcp', 'azure', 'alicloud')"`
	DisplayName string
	Status      string `gorm:"index;type:ENUM('active', 'inactive')"`

	Regions []*Region // A provider has no or many regions.
}

type Region struct {
	gorm.Model

	Name              string `gorm:"index:idx_name_provider,unique"` // logical name, such as: ap-northeast-www-1
	RegionName        string `gorm:"index"`                          // region name, such as: ap-northeast-1
	ProviderId        uint64 `gorm:"index"`
	DisplayName       string
	CountryCode       string `gorm:"index"` // country code(https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). such as: CN, US.
	Area              string `gorm:"index"`
	Priority          int32  `gorm:"default:999999"`
	Status            string `gorm:"index;type:ENUM('active', 'inactive')"`
	IsPublic          *bool  `gorm:"default:true"`
	DefaultBackupTime string `gorm:"default:'00:00'"`
	ServiceAddress    string `gorm:"default:'https://username:password@nice.example.com:443/path/to/whatever?query=file#fragment'"`

	Provider      Provider        // A region belongs to a provider.
	RegionTenants []*RegionTenant // A region has no or many region tenants.
}

type RegionTenant struct {
	gorm.Model

	RegionId uint64 `gorm:"index"`
	TenantId uint64 `gorm:"index"`
}

// 1. private logical region
// 2. default not public region
// 3. public region
func SelectRegionByOrgId(ctx context.Context, regions []*Region, orgId uint64) (*Region, bizerr.BizError) {
	cloudRegions := make([]*Region, 0, 1)
	for _, r := range regions {
		// public region
		if r.IsPublic != nil && *r.IsPublic {
			cloudRegions = append(cloudRegions, r)
			continue
		}

		// only find one private logical region for specified org
		if _, open := lo.Find(r.RegionTenants, func(rt *RegionTenant) bool {
			return rt.TenantId == orgId
		}); open {
			cloudRegions = append(cloudRegions, r)
		}
	}

	switch len(cloudRegions) {
	case 0:
		return nil, bizerr.New(bizerr.ErrCodeParams, "region is not supported for org %d", orgId)
	case 1:
		return cloudRegions[0], nil
	case 2:
		// find private logical region
		for _, r := range cloudRegions {
			if r.Name != r.RegionName {
				return r, nil
			}
		}
		return nil, bizerr.New(
			bizerr.ErrCodeOptimisticLockConflict,
			"not found private logical region: region=%v",
			cloudRegions,
		)
	default:
		return nil, bizerr.New(
			bizerr.ErrCodeOptimisticLockConflict,
			"more than one logical region found: regions=%v",
			cloudRegions,
		)
	}
}
