package repository

import (
	"errors"
	"sort"
	"strings"

	"hyapp-admin-server/internal/appctx"
	"hyapp-admin-server/internal/model"

	"gorm.io/gorm"
)

type AppAccess struct {
	Mode     string
	AppCodes []string
}

func (access AppAccess) Allows(appCode string) bool {
	appCode = strings.ToLower(strings.TrimSpace(appCode))
	if appCode == "" {
		return false
	}
	if access.Mode == model.UserAppScopeModeAll {
		return true
	}
	for _, allowed := range access.AppCodes {
		if allowed == appCode {
			return true
		}
	}
	return false
}

func (s *Store) AppAccessForUser(userID uint) (AppAccess, error) {
	if userID == 0 {
		return AppAccess{Mode: model.UserAppScopeModeNone}, nil
	}
	var user model.User
	if err := s.db.Select("id", "app_scope_mode").First(&user, userID).Error; err != nil {
		return AppAccess{}, err
	}
	mode := normalizeAppScopeMode(user.AppScopeMode)
	if mode != model.UserAppScopeModeSelected {
		return AppAccess{Mode: mode}, nil
	}
	var scopes []model.UserAppScope
	if err := s.db.Where("user_id = ?", userID).Order("app_code ASC").Find(&scopes).Error; err != nil {
		return AppAccess{}, err
	}
	appCodes := make([]string, 0, len(scopes))
	for _, scope := range scopes {
		appCodes = append(appCodes, scope.AppCode)
	}
	return AppAccess{Mode: mode, AppCodes: appCodes}, nil
}

func (s *Store) ReplaceUserAppScopes(userID uint, mode string, appCodes []string) (AppAccess, error) {
	if userID == 0 {
		return AppAccess{}, errors.New("user id is required")
	}
	rawMode := strings.ToLower(strings.TrimSpace(mode))
	if rawMode != model.UserAppScopeModeAll && rawMode != model.UserAppScopeModeSelected && rawMode != model.UserAppScopeModeNone {
		return AppAccess{}, errors.New("app scope mode must be all, selected, or none")
	}
	mode = rawMode
	normalizedCodes := normalizeAppCodes(appCodes)
	if mode == model.UserAppScopeModeSelected && len(normalizedCodes) == 0 {
		return AppAccess{}, errors.New("selected app scope requires app codes")
	}
	if mode != model.UserAppScopeModeSelected {
		normalizedCodes = nil
	}

	err := s.db.Transaction(func(tx *gorm.DB) error {
		var user model.User
		if err := tx.First(&user, userID).Error; err != nil {
			return err
		}
		// 先删除旧明细再更新模式，使 all/none 不残留会被未来代码误读的 selected 行。
		if err := tx.Where("user_id = ?", userID).Delete(&model.UserAppScope{}).Error; err != nil {
			return err
		}
		if len(normalizedCodes) > 0 {
			scopes := make([]model.UserAppScope, 0, len(normalizedCodes))
			for _, appCode := range normalizedCodes {
				scopes = append(scopes, model.UserAppScope{UserID: userID, AppCode: appCode})
			}
			if err := tx.Create(&scopes).Error; err != nil {
				return err
			}
		}
		return tx.Model(&user).Update("app_scope_mode", mode).Error
	})
	if err != nil {
		return AppAccess{}, err
	}
	return AppAccess{Mode: mode, AppCodes: normalizedCodes}, nil
}

func normalizeAppScopeMode(mode string) string {
	switch strings.ToLower(strings.TrimSpace(mode)) {
	case model.UserAppScopeModeAll:
		return model.UserAppScopeModeAll
	case model.UserAppScopeModeSelected:
		return model.UserAppScopeModeSelected
	default:
		return model.UserAppScopeModeNone
	}
}

func normalizeAppCodes(appCodes []string) []string {
	seen := make(map[string]struct{}, len(appCodes))
	out := make([]string, 0, len(appCodes))
	for _, value := range appCodes {
		// appctx.Normalize 会把空值回退 lalu；App scope 的空值代表无授权，必须先独立过滤。
		value = strings.ToLower(strings.TrimSpace(value))
		if value == "" {
			continue
		}
		value = appctx.Normalize(value)
		if _, ok := seen[value]; ok {
			continue
		}
		seen[value] = struct{}{}
		out = append(out, value)
	}
	sort.Strings(out)
	return out
}
