package user

import (
	"context"
	"strings"
	"unicode/utf8"

	"hyapp/pkg/appcode"
	"hyapp/pkg/xerr"
	userdomain "hyapp/services/user-service/internal/domain/user"
)

const (
	maxCountryNameLength        = 128
	maxCountryDisplayNameLength = 128
	maxCountryFlagLength        = 16
	maxRegionNameLength         = 128
)

// UpdateCountry 修改国家展示字段；国家码不允许原地修改。
func (s *Service) UpdateCountry(ctx context.Context, command userdomain.UpdateCountryCommand) (userdomain.Country, error) {
	command = normalizeUpdateCountryCommand(command)
	command.AppCode = appcode.FromContext(ctx)
	if command.CountryID <= 0 {
		return userdomain.Country{}, xerr.New(xerr.InvalidArgument, "country_id is required")
	}
	if err := validateCountryMetadata(command.CountryName, command.ISOAlpha3, command.ISONumeric, command.CountryDisplayName, command.PhoneCountryCode, command.Flag); err != nil {
		return userdomain.Country{}, err
	}
	if strings.TrimSpace(command.RequestID) == "" {
		return userdomain.Country{}, xerr.New(xerr.InvalidArgument, "request_id is required")
	}
	if s.countryAdminRepository == nil {
		return userdomain.Country{}, xerr.New(xerr.Unavailable, "country repository is not configured")
	}

	command.NowMs = s.now().UnixMilli()
	return s.countryAdminRepository.UpdateCountry(ctx, command)
}

// ListCountries 返回国家主数据；enabled 为空时返回全部国家。
func (s *Service) ListCountries(ctx context.Context, enabled *bool) ([]userdomain.Country, error) {
	if s.countryAdminRepository == nil {
		return nil, xerr.New(xerr.Unavailable, "country repository is not configured")
	}

	return s.countryAdminRepository.ListCountries(ctx, userdomain.CountryFilter{AppCode: appcode.FromContext(ctx), Enabled: enabled})
}

// ListRegistrationCountries 返回 App 注册页可选国家列表。
func (s *Service) ListRegistrationCountries(ctx context.Context) ([]userdomain.Country, error) {
	if s.countryRegionRepository == nil {
		return nil, xerr.New(xerr.Unavailable, "country repository is not configured")
	}

	return s.countryRegionRepository.ListRegistrationCountries(ctx)
}

// UpdateRegion 修改区域展示字段，不改变国家归属。
func (s *Service) UpdateRegion(ctx context.Context, regionID int64, regionCode string, name string, sortOrder int32, operatorUserID int64, requestID string) (userdomain.Region, error) {
	regionCode = userdomain.NormalizeRegionCode(regionCode)
	name = strings.TrimSpace(name)
	requestID = strings.TrimSpace(requestID)
	if regionID <= 0 {
		return userdomain.Region{}, xerr.New(xerr.InvalidArgument, "region_id is required")
	}
	if err := validateRegionPayload(regionCode, name); err != nil {
		return userdomain.Region{}, err
	}
	if requestID == "" {
		return userdomain.Region{}, xerr.New(xerr.InvalidArgument, "request_id is required")
	}
	if s.regionAdminRepository == nil {
		return userdomain.Region{}, xerr.New(xerr.Unavailable, "region repository is not configured")
	}

	return s.regionAdminRepository.UpdateRegion(ctx, userdomain.UpdateRegionCommand{
		AppCode:        appcode.FromContext(ctx),
		RegionID:       regionID,
		RegionCode:     regionCode,
		Name:           name,
		SortOrder:      sortOrder,
		OperatorUserID: operatorUserID,
		RequestID:      requestID,
		NowMs:          s.now().UnixMilli(),
	})
}

// ListRegions 返回区域主数据，status 为空时返回全部。
func (s *Service) ListRegions(ctx context.Context, status string) ([]userdomain.Region, error) {
	status = strings.TrimSpace(status)
	if status != "" && status != userdomain.RegionStatusActive && status != userdomain.RegionStatusDisabled {
		return nil, xerr.New(xerr.InvalidArgument, "region status is invalid")
	}
	if s.regionAdminRepository == nil {
		return nil, xerr.New(xerr.Unavailable, "region repository is not configured")
	}

	return s.regionAdminRepository.ListRegions(ctx, userdomain.RegionFilter{AppCode: appcode.FromContext(ctx), Status: status})
}

// GetRegion 返回单个区域及其 active 国家列表。
func (s *Service) GetRegion(ctx context.Context, regionID int64) (userdomain.Region, error) {
	if regionID <= 0 {
		return userdomain.Region{}, xerr.New(xerr.InvalidArgument, "region_id is required")
	}
	if s.regionAdminRepository == nil {
		return userdomain.Region{}, xerr.New(xerr.Unavailable, "region repository is not configured")
	}

	return s.regionAdminRepository.GetRegion(ctx, regionID)
}

// ReplaceRegionCountries 原子替换区域国家归属，并为受影响国家创建用户区域重算任务。
func (s *Service) ReplaceRegionCountries(ctx context.Context, regionID int64, countries []string, operatorUserID int64, requestID string) (userdomain.Region, error) {
	requestID = strings.TrimSpace(requestID)
	if regionID <= 0 {
		return userdomain.Region{}, xerr.New(xerr.InvalidArgument, "region_id is required")
	}
	normalizedCountries, err := normalizeCountryList(countries)
	if err != nil {
		return userdomain.Region{}, err
	}
	if requestID == "" {
		return userdomain.Region{}, xerr.New(xerr.InvalidArgument, "request_id is required")
	}
	if s.regionAdminRepository == nil {
		return userdomain.Region{}, xerr.New(xerr.Unavailable, "region repository is not configured")
	}

	return s.regionAdminRepository.ReplaceRegionCountries(ctx, userdomain.ReplaceRegionCountriesCommand{
		AppCode:        appcode.FromContext(ctx),
		RegionID:       regionID,
		Countries:      normalizedCountries,
		OperatorUserID: operatorUserID,
		RequestID:      requestID,
		NowMs:          s.now().UnixMilli(),
	})
}

func normalizeUpdateCountryCommand(command userdomain.UpdateCountryCommand) userdomain.UpdateCountryCommand {
	command.CountryName = strings.TrimSpace(command.CountryName)
	command.ISOAlpha3 = userdomain.NormalizeISOAlpha3(command.ISOAlpha3)
	command.ISONumeric = userdomain.NormalizeISONumeric(command.ISONumeric)
	command.CountryDisplayName = strings.TrimSpace(command.CountryDisplayName)
	command.PhoneCountryCode = userdomain.NormalizePhoneCountryCode(command.PhoneCountryCode)
	command.Flag = strings.TrimSpace(command.Flag)
	command.RequestID = strings.TrimSpace(command.RequestID)
	return command
}

func validateCountryMetadata(countryName string, isoAlpha3 string, isoNumeric string, countryDisplayName string, phoneCountryCode string, flag string) error {
	if countryName == "" || utf8.RuneCountInString(countryName) > maxCountryNameLength {
		return xerr.New(xerr.InvalidArgument, "country_name length is invalid")
	}
	if !userdomain.ValidISOAlpha3(isoAlpha3) {
		return xerr.New(xerr.InvalidArgument, "iso_alpha3 is invalid")
	}
	if !userdomain.ValidISONumeric(isoNumeric) {
		return xerr.New(xerr.InvalidArgument, "iso_numeric is invalid")
	}
	if countryDisplayName == "" || utf8.RuneCountInString(countryDisplayName) > maxCountryDisplayNameLength {
		return xerr.New(xerr.InvalidArgument, "country_display_name length is invalid")
	}
	if !userdomain.ValidPhoneCountryCode(phoneCountryCode) {
		return xerr.New(xerr.InvalidArgument, "phone_country_code is invalid")
	}
	if utf8.RuneCountInString(flag) > maxCountryFlagLength {
		return xerr.New(xerr.InvalidArgument, "flag is too long")
	}

	return nil
}

func validateRegionPayload(regionCode string, name string) error {
	if !userdomain.ValidRegionCode(regionCode) {
		return xerr.New(xerr.InvalidArgument, "region_code is invalid")
	}
	if userdomain.IsRetiredRegionCode(regionCode) {
		return xerr.New(xerr.InvalidArgument, "region has been retired")
	}
	if name == "" || utf8.RuneCountInString(name) > maxRegionNameLength {
		return xerr.New(xerr.InvalidArgument, "region name length is invalid")
	}

	return nil
}

func normalizeCountryList(countries []string) ([]string, error) {
	if len(countries) == 0 {
		return nil, xerr.New(xerr.InvalidArgument, "countries is required")
	}
	seen := make(map[string]struct{}, len(countries))
	result := make([]string, 0, len(countries))
	for _, country := range countries {
		country = userdomain.NormalizeCountryCode(country)
		if !userdomain.ValidCountryCode(country) {
			return nil, xerr.New(xerr.InvalidArgument, "country_code must use 2 to 3 uppercase letters")
		}
		if _, exists := seen[country]; exists {
			return nil, xerr.New(xerr.InvalidArgument, "countries must not contain duplicates")
		}
		seen[country] = struct{}{}
		result = append(result, country)
	}

	return result, nil
}
