package handler

import (
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"net/http"
	"os"
	"path/filepath"
	"strconv"

	"github.com/gin-gonic/gin"

	"github.com/user-management-system/internal/domain"
	"github.com/user-management-system/internal/repository"
)

// AvatarHandler handles avatar upload requests
type AvatarHandler struct {
	userRepo *repository.UserRepository
}

// NewAvatarHandler creates a new AvatarHandler
func NewAvatarHandler(userRepo *repository.UserRepository) *AvatarHandler {
	return &AvatarHandler{userRepo: userRepo}
}

// generateSecureToken generates a secure random token
func generateSecureToken(length int) string {
	bytes := make([]byte, length)
	rand.Read(bytes)
	return hex.EncodeToString(bytes)[:length]
}

// UploadAvatar 上传用户头像
// @Summary 上传用户头像
// @Description 上传并更新用户头像（仅本人或管理员）
// @Tags 用户头像
// @Accept multipart/form-data
// @Produce json
// @Security BearerAuth
// @Param id path int true "用户ID"
// @Param avatar formData file true "头像文件（最大5MB，支持jpg/jpeg/png/gif/webp）"
// @Success 200 {object} Response{data=AvatarResponse} "上传成功"
// @Failure 400 {object} Response "文件无效或大小超限"
// @Failure 401 {object} Response "未认证"
// @Failure 403 {object} Response "无权限"
// @Failure 404 {object} Response "用户不存在"
// @Router /api/v1/users/{id}/avatar [post]
func (h *AvatarHandler) UploadAvatar(c *gin.Context) {
	userIDStr := c.Param("id")
	userID, err := strconv.ParseInt(userIDStr, 10, 64)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "invalid user id"})
		return
	}

	// Get current user from context (set by auth middleware)
	currentUserID := c.GetInt64("user_id")
	if currentUserID == 0 {
		c.JSON(http.StatusUnauthorized, gin.H{"code": 401, "message": "unauthorized"})
		return
	}

	// Check permission: user can only update their own avatar, or admin can update any
	isAdmin := false
	if roles, ok := c.Get("user_roles"); ok {
		for _, role := range roles.([]*domain.Role) {
			if role.Code == "admin" {
				isAdmin = true
				break
			}
		}
	}

	if currentUserID != userID && !isAdmin {
		c.JSON(http.StatusForbidden, gin.H{"code": 403, "message": "permission denied"})
		return
	}

	// Get file from form
	file, err := c.FormFile("avatar")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "no avatar file provided"})
		return
	}

	// Validate file size (max 5MB)
	if file.Size > 5*1024*1024 {
		c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "file size exceeds 5MB limit"})
		return
	}

	// Validate file type
	ext := filepath.Ext(file.Filename)
	allowedExts := map[string]bool{".jpg": true, ".jpeg": true, ".png": true, ".gif": true, ".webp": true}
	if !allowedExts[ext] {
		c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "invalid file type, allowed: jpg, jpeg, png, gif, webp"})
		return
	}

	// Open the uploaded file
	src, err := file.Open()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "failed to open uploaded file"})
		return
	}
	defer src.Close()

	// Generate unique filename
	avatarFilename := fmt.Sprintf("avatar_%d_%s%s", userID, generateSecureToken(8), ext)
	uploadDir := "./uploads/avatars"

	// Create upload directory if not exists
	if err := os.MkdirAll(uploadDir, 0755); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "failed to create upload directory"})
		return
	}

	// Save file to disk
	dstPath := filepath.Join(uploadDir, avatarFilename)
	data := make([]byte, file.Size)
	if _, err := src.Read(data); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "failed to read uploaded file"})
		return
	}
	if err := os.WriteFile(dstPath, data, 0644); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "failed to save avatar file"})
		return
	}

	// Generate avatar URL (in production, this would be a CDN URL)
	avatarURL := fmt.Sprintf("/uploads/avatars/%s", avatarFilename)

	// Update user's avatar in database
	user, err := h.userRepo.GetByID(c.Request.Context(), userID)
	if err != nil {
		// Clean up the uploaded file
		os.Remove(dstPath)
		c.JSON(http.StatusNotFound, gin.H{"code": 404, "message": "user not found"})
		return
	}

	user.Avatar = avatarURL
	if err := h.userRepo.Update(c.Request.Context(), user); err != nil {
		// Clean up the uploaded file
		os.Remove(dstPath)
		c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "failed to update user avatar"})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"code":    0,
		"message": "avatar uploaded successfully",
		"data": gin.H{
			"avatar_url": avatarURL,
			"thumbnail":  avatarURL,
		},
	})
}
