package adminuser

import (
	"errors"
	"fmt"

	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
	"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/response"
)

type Handler struct {
	service *AdminUserService
	audit   shared.OperationLogger
}

func New(store *repository.Store, cfg config.Config, audit shared.OperationLogger) *Handler {
	return &Handler{service: NewService(store, cfg), audit: audit}
}

func (h *Handler) ListUsers(c *gin.Context) {
	options := shared.ListOptions(c)
	users, total, err := h.service.ListUsers(shared.ActorFromContext(c), options)
	if err != nil {
		response.ServerError(c, "获取用户失败")
		return
	}
	response.OK(c, response.Page{Items: shared.UserDTOs(users), Page: options.Page, PageSize: options.PageSize, Total: total})
}

func (h *Handler) CreateUser(c *gin.Context) {
	var req userRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "用户参数不正确")
		return
	}
	user, password, err := h.service.CreateUser(CreateUserInput{
		Username:   req.Username,
		Name:       req.Name,
		Team:       req.Team,
		TeamID:     req.TeamID,
		Status:     req.Status,
		Password:   req.Password,
		MFAEnabled: req.MFAEnabled,
		RoleIDs:    req.RoleIDs,
	})
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	shared.OperationLog(c, h.audit, "create-user", "admin_users", "success", user.Username)
	response.Created(c, gin.H{"user": shared.UserDTO(*user), "initialPassword": password.Password})
}

func (h *Handler) GetUser(c *gin.Context) {
	id, ok := shared.ParseID(c, "id")
	if !ok {
		return
	}
	user, err := h.service.GetUser(id)
	if errors.Is(err, gorm.ErrRecordNotFound) {
		response.NotFound(c, "用户不存在")
		return
	}
	if err != nil {
		response.ServerError(c, "获取用户失败")
		return
	}
	response.OK(c, shared.UserDTO(*user))
}

func (h *Handler) ListUserMoneyScopes(c *gin.Context) {
	id, ok := shared.ParseID(c, "id")
	if !ok {
		return
	}
	scopes, err := h.service.ListUserMoneyScopes(id)
	if err != nil {
		response.ServerError(c, "获取财务范围失败")
		return
	}
	response.OK(c, gin.H{"items": moneyScopeDTOs(scopes), "total": len(scopes)})
}

func (h *Handler) ListMoneyScopeAssignments(c *gin.Context) {
	assignments, err := h.service.ListMoneyScopeAssignments()
	if err != nil {
		response.ServerError(c, "获取财务范围分配失败")
		return
	}
	items := make([]gin.H, 0, len(assignments))
	for _, item := range assignments {
		items = append(items, gin.H{
			"appCode":     item.AppCode,
			"regionId":    item.RegionID,
			"userId":      item.UserID,
			"userName":    item.UserName,
			"userAccount": item.UserAccount,
			"userStatus":  item.UserStatus,
		})
	}
	response.OK(c, gin.H{"items": items, "total": len(items)})
}

func (h *Handler) ReplaceUserMoneyScopes(c *gin.Context) {
	id, ok := shared.ParseID(c, "id")
	if !ok {
		return
	}
	var req moneyScopeRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "财务范围参数不正确")
		return
	}
	input := make([]MoneyScopeInput, 0, len(req.Scopes))
	for _, item := range req.Scopes {
		input = append(input, MoneyScopeInput{AppCode: item.AppCode, RegionID: item.RegionID})
	}
	scopes, err := h.service.ReplaceUserMoneyScopes(id, input)
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	shared.OperationLog(c, h.audit, "replace-user-money-scopes", "admin_user_money_scopes", "success", fmt.Sprintf("user_id=%d scopes=%d", id, len(scopes)))
	response.OK(c, gin.H{"items": moneyScopeDTOs(scopes), "total": len(scopes)})
}

func (h *Handler) GetUserAppScopes(c *gin.Context) {
	id, ok := shared.ParseID(c, "id")
	if !ok {
		return
	}
	access, err := h.service.GetUserAppScopes(id)
	if errors.Is(err, gorm.ErrRecordNotFound) {
		response.NotFound(c, "用户不存在")
		return
	}
	if err != nil {
		response.ServerError(c, "获取 App 范围失败")
		return
	}
	response.OK(c, appScopeDTO(access))
}

func (h *Handler) ReplaceUserAppScopes(c *gin.Context) {
	id, ok := shared.ParseID(c, "id")
	if !ok {
		return
	}
	var req appScopeRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "App 范围参数不正确")
		return
	}
	access, err := h.service.ReplaceUserAppScopes(id, AppScopeInput{Mode: req.Mode, AppCodes: req.AppCodes})
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	shared.OperationLog(c, h.audit, "replace-user-app-scopes", "admin_user_app_scopes", "success", fmt.Sprintf("user_id=%d mode=%s apps=%d", id, access.Mode, len(access.AppCodes)))
	response.OK(c, appScopeDTO(access))
}

func (h *Handler) UpdateUser(c *gin.Context) {
	id, ok := shared.ParseID(c, "id")
	if !ok {
		return
	}
	var req updateUserRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "用户参数不正确")
		return
	}

	user, err := h.service.UpdateUser(id, UpdateUserInput{
		Name:       req.Name,
		Team:       req.Team,
		TeamID:     req.TeamID,
		Status:     req.Status,
		MFAEnabled: req.MFAEnabled,
		RoleIDs:    req.RoleIDs,
	})
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	shared.OperationLog(c, h.audit, "update-user", "admin_users", "success", user.Username)
	response.OK(c, shared.UserDTO(*user))
}

func (h *Handler) UpdateUserStatus(c *gin.Context) {
	id, ok := shared.ParseID(c, "id")
	if !ok {
		return
	}
	var req statusRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "状态参数不正确")
		return
	}
	user, err := h.service.UpdateUserStatus(id, UserStatusInput{Status: req.Status})
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	shared.OperationLog(c, h.audit, "update-user-status", "admin_users", "success", fmt.Sprintf("%s -> %s", user.Username, req.Status))
	response.OK(c, shared.UserDTO(*user))
}

func (h *Handler) ResetUserPassword(c *gin.Context) {
	id, ok := shared.ParseID(c, "id")
	if !ok {
		return
	}
	var req resetPasswordRequest
	_ = c.ShouldBindJSON(&req)
	password, err := h.service.ResetUserPassword(id, req.Password)
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	shared.OperationLog(c, h.audit, "reset-user-password", "admin_users", "success", fmt.Sprintf("user_id=%d", id))
	response.OK(c, gin.H{"reset": true, "initialPassword": password.Password})
}

func (h *Handler) BatchUpdateUserStatus(c *gin.Context) {
	var req batchStatusRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "批量状态参数不正确")
		return
	}
	if err := h.service.BatchUpdateUserStatus(BatchUserStatusInput{IDs: req.IDs, Status: req.Status}); err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	shared.OperationLog(c, h.audit, "batch-update-user-status", "admin_users", "success", fmt.Sprintf("%d users -> %s", len(req.IDs), req.Status))
	response.OK(c, gin.H{"updated": len(req.IDs)})
}

func (h *Handler) ExportUsers(c *gin.Context) {
	export, err := h.service.ExportUsers(shared.ActorFromContext(c), shared.ListOptions(c))
	if err != nil {
		response.ServerError(c, "导出用户失败")
		return
	}

	shared.OperationLog(c, h.audit, "export-users", "admin_users", "success", fmt.Sprintf("%d users", export.Count))
	c.Header("Content-Disposition", "attachment; filename="+export.FileName)
	c.Header("Content-Type", "text/csv; charset=utf-8")
	c.Writer.WriteHeader(200)
	_, _ = c.Writer.Write(export.Content)
}

func moneyScopeDTOs(scopes []model.UserMoneyScope) []gin.H {
	out := make([]gin.H, 0, len(scopes))
	for _, scope := range scopes {
		out = append(out, gin.H{
			"appCode":  scope.AppCode,
			"regionId": scope.RegionID,
			"userId":   scope.UserID,
		})
	}
	return out
}

func appScopeDTO(access repository.AppAccess) gin.H {
	appCodes := access.AppCodes
	if appCodes == nil {
		appCodes = []string{}
	}
	return gin.H{"mode": access.Mode, "appCodes": appCodes}
}
