package handler

import (
	"errors"
	"net/http"
	"strings"

	"juhe-factory/api/internal/model"
	"juhe-factory/api/internal/service"

	"github.com/gin-gonic/gin"
)

const adminContextKey = "admin_user"

type AdminAuth struct{ service *service.Auth }

func NewAdminAuth(service *service.Auth) *AdminAuth { return &AdminAuth{service: service} }

func (h *AdminAuth) Login(c *gin.Context) {
	var body struct {
		Username string `json:"username" binding:"required"`
		Password string `json:"password" binding:"required"`
	}
	if err := c.ShouldBindJSON(&body); err != nil {
		fail(c, http.StatusBadRequest, "invalid_request", "请输入管理员账号和密码")
		return
	}
	pair, err := h.service.Login(body.Username, body.Password)
	if err != nil {
		fail(c, http.StatusUnauthorized, "invalid_credentials", err.Error())
		return
	}
	c.JSON(http.StatusOK, gin.H{"data": pair})
}

func (h *AdminAuth) Refresh(c *gin.Context) {
	var body struct {
		RefreshToken string `json:"refresh_token" binding:"required"`
	}
	if err := c.ShouldBindJSON(&body); err != nil {
		fail(c, http.StatusBadRequest, "invalid_request", "缺少刷新令牌")
		return
	}
	pair, err := h.service.Refresh(body.RefreshToken)
	if err != nil {
		fail(c, http.StatusUnauthorized, "invalid_refresh_token", err.Error())
		return
	}
	c.JSON(http.StatusOK, gin.H{"data": pair})
}

func (h *AdminAuth) Logout(c *gin.Context) {
	var body struct {
		RefreshToken string `json:"refresh_token"`
	}
	_ = c.ShouldBindJSON(&body)
	if err := h.service.Logout(body.RefreshToken); err != nil {
		fail(c, http.StatusInternalServerError, "logout_failed", "退出登录失败")
		return
	}
	c.Status(http.StatusNoContent)
}

func (h *AdminAuth) ChangePassword(c *gin.Context) {
	var body struct {
		CurrentPassword string `json:"current_password" binding:"required"`
		NewPassword     string `json:"new_password" binding:"required"`
		ConfirmPassword string `json:"confirm_password" binding:"required"`
	}
	if err := c.ShouldBindJSON(&body); err != nil {
		fail(c, http.StatusBadRequest, "invalid_request", "请完整填写密码")
		return
	}
	admin := currentAdmin(c)
	if err := h.service.ChangePassword(admin.ID, body.CurrentPassword, body.NewPassword, body.ConfirmPassword); err != nil {
		if errors.Is(err, service.ErrReauthFailed) || errors.Is(err, service.ErrAdminPasswordConfirmation) || errors.Is(err, service.ErrAdminPasswordLength) {
			_ = h.service.Audit(admin, "password_change_failed", "admin-auth", admin.ID.String(), err.Error(), c.ClientIP(), c.GetString("trace_id"), nil)
			fail(c, http.StatusBadRequest, "password_invalid", err.Error())
			return
		}
		fail(c, http.StatusInternalServerError, "password_update_failed", "密码修改失败，请稍后重试")
		return
	}
	_ = h.service.Audit(admin, "password_changed", "admin-auth", admin.ID.String(), "", c.ClientIP(), c.GetString("trace_id"), nil)
	c.Status(http.StatusNoContent)
}

func (h *AdminAuth) Middleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		raw := strings.TrimSpace(strings.TrimPrefix(c.GetHeader("Authorization"), "Bearer "))
		admin, err := h.service.Authenticate(raw)
		if err != nil {
			fail(c, http.StatusUnauthorized, "unauthorized", err.Error())
			c.Abort()
			return
		}
		c.Set(adminContextKey, admin)
		c.Next()
	}
}

func currentAdmin(c *gin.Context) *model.AdminUser {
	value, _ := c.Get(adminContextKey)
	admin, _ := value.(*model.AdminUser)
	return admin
}

func fail(c *gin.Context, status int, code, message string) {
	c.JSON(status, gin.H{"code": code, "message": message, "trace_id": c.GetString("trace_id")})
}
