package user

import (
	"context"
	"strings"

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

const (
	maxPushDeviceIDLen   = 128
	maxPushTokenLen      = 512
	maxPushProviderLen   = 32
	maxPushPlatformLen   = 16
	maxPushAppVersionLen = 64
	maxPushLanguageLen   = 32
	maxPushTimezoneLen   = 64
)

// BindPushToken 绑定当前登录用户的系统推送 token。
// 该能力不影响 App 进入主界面；失败只影响离线消息触达。
func (s *Service) BindPushToken(ctx context.Context, userID int64, deviceID string, pushToken string, provider string, platform string, appVersion string, language string, timezone string, requestID string) (int64, error) {
	if s.deviceRepository == nil {
		return 0, xerr.New(xerr.Unavailable, "device repository is not configured")
	}
	if err := s.requireActiveCompletedUser(ctx, userID); err != nil {
		return 0, err
	}

	command, err := normalizePushTokenCommand(ctx, userID, deviceID, pushToken, provider, platform, appVersion, language, timezone, requestID, s.now().UnixMilli())
	if err != nil {
		return 0, err
	}
	if err := s.deviceRepository.BindPushToken(ctx, command); err != nil {
		return 0, err
	}

	return command.UpdatedAtMs, nil
}

// DeletePushToken 失效当前登录用户某个设备的推送 token。
func (s *Service) DeletePushToken(ctx context.Context, userID int64, deviceID string, pushToken string, requestID string) (bool, int64, error) {
	if s.deviceRepository == nil {
		return false, 0, xerr.New(xerr.Unavailable, "device repository is not configured")
	}
	if userID <= 0 {
		return false, 0, xerr.New(xerr.InvalidArgument, "user_id is required")
	}

	deviceID = strings.TrimSpace(deviceID)
	pushToken = strings.TrimSpace(pushToken)
	if deviceID == "" || len(deviceID) > maxPushDeviceIDLen {
		return false, 0, xerr.New(xerr.InvalidArgument, "device_id is invalid")
	}
	if len(pushToken) > maxPushTokenLen {
		return false, 0, xerr.New(xerr.InvalidArgument, "push_token is invalid")
	}

	nowMs := s.now().UnixMilli()
	deleted, err := s.deviceRepository.DeletePushToken(ctx, userdomain.DeletePushTokenCommand{
		AppCode:     appcode.FromContext(ctx),
		UserID:      userID,
		DeviceID:    deviceID,
		PushToken:   pushToken,
		RequestID:   strings.TrimSpace(requestID),
		UpdatedAtMs: nowMs,
	})
	if err != nil {
		return false, 0, err
	}

	return deleted, nowMs, nil
}

func (s *Service) requireActiveCompletedUser(ctx context.Context, userID int64) error {
	if userID <= 0 {
		return xerr.New(xerr.InvalidArgument, "user_id is required")
	}
	user, err := s.GetUser(ctx, userID)
	if err != nil {
		return err
	}
	if !user.ProfileCompleted {
		return xerr.New(xerr.ProfileRequired, "profile required")
	}
	if user.Status != userdomain.StatusActive {
		return xerr.New(xerr.UserDisabled, "user disabled")
	}
	return nil
}

func normalizePushTokenCommand(ctx context.Context, userID int64, deviceID string, pushToken string, provider string, platform string, appVersion string, language string, timezone string, requestID string, nowMs int64) (userdomain.PushTokenCommand, error) {
	deviceID = strings.TrimSpace(deviceID)
	pushToken = strings.TrimSpace(pushToken)
	platform = strings.ToLower(strings.TrimSpace(platform))
	provider = strings.ToLower(strings.TrimSpace(provider))
	if provider == "" {
		provider = defaultPushProvider(platform)
	}

	if deviceID == "" || len(deviceID) > maxPushDeviceIDLen {
		return userdomain.PushTokenCommand{}, xerr.New(xerr.InvalidArgument, "device_id is invalid")
	}
	if pushToken == "" || len(pushToken) > maxPushTokenLen {
		return userdomain.PushTokenCommand{}, xerr.New(xerr.InvalidArgument, "push_token is invalid")
	}
	if provider == "" || len(provider) > maxPushProviderLen {
		return userdomain.PushTokenCommand{}, xerr.New(xerr.InvalidArgument, "provider is invalid")
	}
	if platform != "android" && platform != "ios" {
		return userdomain.PushTokenCommand{}, xerr.New(xerr.InvalidArgument, "platform is invalid")
	}

	appVersion = strings.TrimSpace(appVersion)
	language = strings.TrimSpace(language)
	timezone = strings.TrimSpace(timezone)
	if len(appVersion) > maxPushAppVersionLen || len(language) > maxPushLanguageLen || len(timezone) > maxPushTimezoneLen {
		return userdomain.PushTokenCommand{}, xerr.New(xerr.InvalidArgument, "push token metadata is invalid")
	}

	return userdomain.PushTokenCommand{
		AppCode:     appcode.FromContext(ctx),
		UserID:      userID,
		DeviceID:    deviceID,
		PushToken:   pushToken,
		Provider:    provider,
		Platform:    platform,
		AppVersion:  appVersion,
		Language:    language,
		Timezone:    timezone,
		RequestID:   strings.TrimSpace(requestID),
		UpdatedAtMs: nowMs,
	}, nil
}

func defaultPushProvider(platform string) string {
	switch platform {
	case "android":
		return "fcm"
	case "ios":
		return "apns"
	default:
		return ""
	}
}
