package adminuser

import (
	"bytes"
	"encoding/csv"
	"fmt"
	"strings"
	"time"

	"hyapp-admin-server/internal/config"
	"hyapp-admin-server/internal/model"
	"hyapp-admin-server/internal/modules/shared"
	"hyapp-admin-server/internal/repository"
	"hyapp-admin-server/internal/security"
)

const defaultInitialPassword = "Hyapp@123456"

type AdminUserService struct {
	store *repository.Store
	cfg   config.Config
}

type CreateUserInput struct {
	Username   string
	Name       string
	Team       string
	TeamID     *uint
	Status     string
	Password   string
	MFAEnabled bool
	RoleIDs    []uint
}

type UpdateUserInput struct {
	Name       *string
	Team       *string
	TeamID     *uint
	Status     *string
	MFAEnabled *bool
	RoleIDs    *[]uint
}

type UserStatusInput struct {
	Status string
}

type BatchUserStatusInput struct {
	IDs    []uint
	Status string
}

type PasswordResult struct {
	Password string
}

type UserExport struct {
	FileName string
	Content  []byte
	Count    int
}

type MoneyScopeInput struct {
	AppCode  string
	RegionID int64
}

type AppScopeInput struct {
	Mode     string
	AppCodes []string
}

func NewService(store *repository.Store, cfg config.Config) *AdminUserService {
	return &AdminUserService{store: store, cfg: cfg}
}

func (s *AdminUserService) ListUsers(actor shared.Actor, options repository.ListOptions) ([]model.User, int64, error) {
	access, err := s.store.DataAccessForUser(actor.UserID)
	if err != nil {
		return nil, 0, err
	}
	return s.store.ListUsersWithAccess(options, access)
}

func (s *AdminUserService) CreateUser(input CreateUserInput) (*model.User, PasswordResult, error) {
	password := strings.TrimSpace(input.Password)
	if password == "" {
		password = defaultInitialPassword
	}
	hash, err := security.HashPassword(password)
	if err != nil {
		return nil, PasswordResult{}, err
	}
	teamID, teamName, err := s.store.NormalizeUserTeam(input.TeamID, input.Team)
	if err != nil {
		return nil, PasswordResult{}, err
	}

	user := model.User{
		Username:     strings.TrimSpace(input.Username),
		Name:         strings.TrimSpace(input.Name),
		PasswordHash: hash,
		Team:         teamName,
		TeamID:       teamID,
		Status:       shared.DefaultString(input.Status, model.UserStatusActive),
		// 新用户先以零 App 权限落库，后续独立 scope 请求即使失败也不会产生短暂的全 App 越权窗口。
		AppScopeMode: model.UserAppScopeModeNone,
		MFAEnabled:   input.MFAEnabled,
	}
	if err := s.store.CreateUser(&user, input.RoleIDs); err != nil {
		return nil, PasswordResult{}, err
	}
	created, err := s.store.FindUserByID(user.ID)
	if err != nil {
		return nil, PasswordResult{}, err
	}
	return created, PasswordResult{Password: password}, nil
}

func (s *AdminUserService) GetUser(id uint) (*model.User, error) {
	return s.store.FindUserByID(id)
}

func (s *AdminUserService) ListUserMoneyScopes(id uint) ([]model.UserMoneyScope, error) {
	return s.store.ListUserMoneyScopes(id)
}

// MoneyScopeAssignment 描述某个 (App, 区域) 当前被哪位后台用户持有，用于分配弹窗把他人已占的区域置灰。
type MoneyScopeAssignment struct {
	UserID      uint
	UserName    string
	UserAccount string
	UserStatus  string
	AppCode     string
	RegionID    int64
}

func (s *AdminUserService) ListMoneyScopeAssignments() ([]MoneyScopeAssignment, error) {
	scopes, err := s.store.ListAllUserMoneyScopes()
	if err != nil {
		return nil, err
	}
	userIDs := make([]uint, 0, len(scopes))
	for _, scope := range scopes {
		userIDs = append(userIDs, scope.UserID)
	}
	users, err := s.store.FindUsersByIDs(userIDs)
	if err != nil {
		return nil, err
	}
	out := make([]MoneyScopeAssignment, 0, len(scopes))
	for _, scope := range scopes {
		assignment := MoneyScopeAssignment{UserID: scope.UserID, AppCode: scope.AppCode, RegionID: scope.RegionID}
		if user, ok := users[scope.UserID]; ok {
			assignment.UserName = user.Name
			assignment.UserAccount = user.Username
			assignment.UserStatus = user.Status
		}
		out = append(out, assignment)
	}
	return out, nil
}

func (s *AdminUserService) ReplaceUserMoneyScopes(id uint, input []MoneyScopeInput) ([]model.UserMoneyScope, error) {
	scopes := make([]model.UserMoneyScope, 0, len(input))
	for _, item := range input {
		scopes = append(scopes, model.UserMoneyScope{
			AppCode:  strings.TrimSpace(item.AppCode),
			RegionID: item.RegionID,
		})
	}
	if err := s.store.ReplaceUserMoneyScopes(id, scopes); err != nil {
		return nil, err
	}
	return s.store.ListUserMoneyScopes(id)
}

func (s *AdminUserService) GetUserAppScopes(id uint) (repository.AppAccess, error) {
	return s.store.AppAccessForUser(id)
}

func (s *AdminUserService) ReplaceUserAppScopes(id uint, input AppScopeInput) (repository.AppAccess, error) {
	return s.store.ReplaceUserAppScopes(id, input.Mode, input.AppCodes)
}

func (s *AdminUserService) UpdateUser(id uint, input UpdateUserInput) (*model.User, error) {
	updates := map[string]any{}
	if input.Name != nil {
		updates["name"] = strings.TrimSpace(*input.Name)
	}
	if input.Team != nil {
		teamID, teamName, err := s.store.NormalizeUserTeam(input.TeamID, *input.Team)
		if err != nil {
			return nil, err
		}
		updates["team"] = teamName
		updates["team_id"] = teamID
	} else if input.TeamID != nil {
		teamID, teamName, err := s.store.NormalizeUserTeam(input.TeamID, "")
		if err != nil {
			return nil, err
		}
		updates["team"] = teamName
		updates["team_id"] = teamID
	}
	if input.Status != nil {
		updates["status"] = strings.TrimSpace(*input.Status)
	}
	if input.MFAEnabled != nil {
		updates["mfa_enabled"] = *input.MFAEnabled
	}
	return s.store.UpdateUser(id, updates, input.RoleIDs)
}

func (s *AdminUserService) UpdateUserStatus(id uint, input UserStatusInput) (*model.User, error) {
	return s.store.UpdateUserStatus(id, input.Status)
}

func (s *AdminUserService) ResetUserPassword(id uint, password string) (PasswordResult, error) {
	value := strings.TrimSpace(password)
	if value == "" {
		value = defaultInitialPassword
	}
	if err := s.store.ResetUserPassword(id, value); err != nil {
		return PasswordResult{}, err
	}
	return PasswordResult{Password: value}, nil
}

func (s *AdminUserService) BatchUpdateUserStatus(input BatchUserStatusInput) error {
	return s.store.BatchUpdateUserStatus(input.IDs, input.Status)
}

func (s *AdminUserService) ExportUsers(actor shared.Actor, options repository.ListOptions) (UserExport, error) {
	access, err := s.store.DataAccessForUser(actor.UserID)
	if err != nil {
		return UserExport{}, err
	}
	users, err := s.store.ExportUsersWithAccess(options, access)
	if err != nil {
		return UserExport{}, err
	}

	var buf bytes.Buffer
	writer := csv.NewWriter(&buf)
	_ = writer.Write([]string{"ID", "账号", "姓名", "角色", "团队", "状态", "MFA", "最后登录"})
	for _, user := range users {
		lastLogin := ""
		if user.LastLoginAtMS != nil {
			lastLogin = time.UnixMilli(*user.LastLoginAtMS).UTC().Format("2006-01-02 15:04:05")
		}
		_ = writer.Write([]string{
			fmt.Sprintf("%d", user.ID),
			user.Username,
			user.Name,
			strings.Join(roleNames(user.Roles), "、"),
			shared.UserTeamName(user),
			user.Status,
			shared.BoolText(user.MFAEnabled),
			lastLogin,
		})
	}
	writer.Flush()
	if err := writer.Error(); err != nil {
		return UserExport{}, err
	}
	return UserExport{FileName: "hyapp-users.csv", Content: buf.Bytes(), Count: len(users)}, nil
}

func roleNames(roles []model.Role) []string {
	names := make([]string, 0, len(roles))
	for _, role := range roles {
		names = append(names, role.Name)
	}
	return names
}
