package api

import (
	"net/http"
	"strconv"

	"github.com/gin-gonic/gin"

	"chaitin.cn/patronus/safeline-2/management/webserver/api/response"
	"chaitin.cn/patronus/safeline-2/management/webserver/model"
	"chaitin.cn/patronus/safeline-2/management/webserver/pkg/database"
)

type CreateUserRequest struct {
	Username string `json:"username" binding:"required,min=3,max=32"`
	Password string `json:"password" binding:"required,min=6,max=64"`
	Role     string `json:"role" binding:"required,oneof=admin operator viewer"`
	Comment  string `json:"comment"`
}

type UpdateUserRequest struct {
	Role    string `json:"role" binding:"omitempty,oneof=admin operator viewer"`
	Comment string `json:"comment"`
	Enabled *bool  `json:"enabled"`
}

type ResetPasswordRequest struct {
	Password string `json:"password" binding:"required,min=6,max=64"`
}

type UserInfo struct {
	ID       uint   `json:"id"`
	Username string `json:"username"`
	Role     string `json:"role"`
	Comment  string `json:"comment"`
	Enabled  bool   `json:"enabled"`
}

func GetUsers(c *gin.Context) {
	db := database.GetDB()
	var users []model.User
	db.Find(&users)

	var result []UserInfo
	for _, u := range users {
		result = append(result, UserInfo{
			ID:       u.ID,
			Username: u.Username,
			Role:     u.Role,
			Comment:  u.Comment,
			Enabled:  u.IsEnabled,
		})
	}

	response.Success(c, gin.H{"data": result})
}

func PostUser(c *gin.Context) {
	var params CreateUserRequest
	if err := c.BindJSON(&params); err != nil {
		logger.Error(err)
		response.Error(c, response.ErrorParamNotOK, http.StatusInternalServerError)
		return
	}

	db := database.GetDB()

	// Check if username already exists
	var existing model.User
	res := db.Where(&model.User{Username: params.Username}).First(&existing)
	if res.RowsAffected > 0 {
		response.Error(c, response.JSONBody{Err: "username_exists", Msg: "Username already exists"}, http.StatusConflict)
		return
	}

	user := model.User{
		Username: params.Username,
		Role:     params.Role,
		Comment:  params.Comment,
	}
	if err := user.SetPassword(params.Password); err != nil {
		logger.Error(err)
		response.Error(c, response.JSONBody{Err: response.ErrInternalError, Msg: "Failed to hash password"}, http.StatusInternalServerError)
		return
	}

	if err := db.Create(&user).Error; err != nil {
		logger.Error(err)
		response.Error(c, response.JSONBody{Err: response.ErrInternalError, Msg: err.Error()}, http.StatusInternalServerError)
		return
	}

	response.Success(c, gin.H{
		"id":       user.ID,
		"username": user.Username,
		"role":     user.Role,
	})
}

func PutUser(c *gin.Context) {
	id, err := strconv.Atoi(c.Param("id"))
	if err != nil {
		response.Error(c, response.ErrorParamNotOK, http.StatusBadRequest)
		return
	}

	var params UpdateUserRequest
	if err := c.BindJSON(&params); err != nil {
		logger.Error(err)
		response.Error(c, response.ErrorParamNotOK, http.StatusInternalServerError)
		return
	}

	db := database.GetDB()
	var user model.User
	res := db.First(&user, id)
	if res.RowsAffected == 0 {
		response.Error(c, response.ErrorDataNotExist, http.StatusNotFound)
		return
	}

	// Prevent disabling the last admin
	if params.Enabled != nil && !*params.Enabled && user.Role == "admin" {
		var adminCount int64
		db.Model(&model.User{}).Where("role = ? AND is_enabled = true", "admin").Count(&adminCount)
		if adminCount <= 1 {
			response.Error(c, response.JSONBody{Err: "last_admin", Msg: "Cannot disable the last admin user"}, http.StatusConflict)
			return
		}
	}

	updates := map[string]interface{}{}
	if params.Role != "" {
		updates["role"] = params.Role
	}
	if params.Comment != "" {
		updates["comment"] = params.Comment
	}
	if params.Enabled != nil {
		updates["is_enabled"] = *params.Enabled
	}

	db.Model(&user).Updates(updates)
	response.Success(c, nil)
}

func DeleteUser(c *gin.Context) {
	id, err := strconv.Atoi(c.Param("id"))
	if err != nil {
		response.Error(c, response.ErrorParamNotOK, http.StatusBadRequest)
		return
	}

	db := database.GetDB()
	var user model.User
	res := db.First(&user, id)
	if res.RowsAffected == 0 {
		response.Error(c, response.ErrorDataNotExist, http.StatusNotFound)
		return
	}

	// Prevent deleting the last admin
	if user.Role == "admin" {
		var adminCount int64
		db.Model(&model.User{}).Where("role = ? AND is_enabled = true", "admin").Count(&adminCount)
		if adminCount <= 1 {
			response.Error(c, response.JSONBody{Err: "last_admin", Msg: "Cannot delete the last admin user"}, http.StatusConflict)
			return
		}
	}

	// Prevent self-deletion
	sessionUser := c.GetInt("user_id")
	if uint(sessionUser) == user.ID {
		response.Error(c, response.JSONBody{Err: "self_delete", Msg: "Cannot delete your own account"}, http.StatusConflict)
		return
	}

	db.Delete(&user)
	response.Success(c, nil)
}

func PostResetPassword(c *gin.Context) {
	id, err := strconv.Atoi(c.Param("id"))
	if err != nil {
		response.Error(c, response.ErrorParamNotOK, http.StatusBadRequest)
		return
	}

	var params ResetPasswordRequest
	if err := c.BindJSON(&params); err != nil {
		logger.Error(err)
		response.Error(c, response.ErrorParamNotOK, http.StatusInternalServerError)
		return
	}

	db := database.GetDB()
	var user model.User
	res := db.First(&user, id)
	if res.RowsAffected == 0 {
		response.Error(c, response.ErrorDataNotExist, http.StatusNotFound)
		return
	}

	if err := user.SetPassword(params.Password); err != nil {
		logger.Error(err)
		response.Error(c, response.JSONBody{Err: response.ErrInternalError, Msg: "Failed to hash password"}, http.StatusInternalServerError)
		return
	}

	db.Model(&user).Update("password_hash", user.PasswordHash)
	response.Success(c, nil)
}

// GetUser returns the current authenticated user info
func GetCurrentUser(c *gin.Context) {
	userId, _ := c.Get("user_id")
	db := database.GetDB()
	var user model.User
	res := db.First(&user, userId)
	if res.RowsAffected == 0 {
		response.Error(c, response.ErrorDataNotExist, http.StatusNotFound)
		return
	}

	response.Success(c, gin.H{
		"id":       user.ID,
		"username": user.Username,
		"role":     user.Role,
	})
}
