package controllers

import (
	"ems-backend/models"
	"ems-backend/services"
	"ems-backend/utils" // Ensure this is imported
	"fmt" // added fmt
	"net/http"
	"regexp"
	"strconv"

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

// --- Role Controller ---

// GetRoles 获取角色列表
func GetRoles(c *gin.Context) {
	// Parse Query Params
	pageNumStr := c.Query("pageNum")
	pageSizeStr := c.Query("pageSize")
	name := c.Query("name")
	roleKey := c.Query("roleKey")
	status := c.Query("status")

	// Base Query
	query := models.DB.Model(&models.SysRole{})

	if name != "" {
		query = query.Where("name LIKE ?", "%"+name+"%")
	}
	if roleKey != "" {
		query = query.Where("role_key LIKE ?", "%"+roleKey+"%")
	}
	if status != "" {
		query = query.Where("status = ?", status)
	}

	// Calculate Total
	var total int64
	query.Count(&total)

	var roles []models.SysRole

	// Pagination
	if pageNumStr != "" && pageSizeStr != "" {
		pageNum, _ := strconv.Atoi(pageNumStr)
		pageSize, _ := strconv.Atoi(pageSizeStr)
		if pageNum < 1 {
			pageNum = 1
		}
		if pageSize < 1 {
			pageSize = 10
		}
		offset := (pageNum - 1) * pageSize
		query = query.Offset(offset).Limit(pageSize)
	}

	if err := query.Find(&roles).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	// ------------------------------------------------------------------
	// Fetch Menu Names for each Role to display in frontend list
	// ------------------------------------------------------------------
	// Create a response structure that includes menu names
	type RoleResponse struct {
		models.SysRole
		MenuNames string `json:"MenuNames"`
	}

	var roleResponses []RoleResponse

	for _, role := range roles {
		var menuNames []string
		// Join sys_role_menus and sys_menus
		// Filter by role_id and type != 'F' (Button)
		models.DB.Table("sys_menus m").
			Joins("JOIN sys_role_menus rm ON rm.menu_id = m.id").
			Where("rm.role_id = ? AND m.type != 'F'", role.ID).
			Pluck("m.name", &menuNames)

		// Limit number of names to avoid too long string, though frontend handles truncation too.
		// Let's just join all non-button menu names.
		// Frontend can truncate with "..."
		
		namesStr := ""
		for i, name := range menuNames {
			if i > 0 {
				namesStr += ", "
			}
			namesStr += name
		}

		roleResponses = append(roleResponses, RoleResponse{
			SysRole:   role,
			MenuNames: namesStr,
		})
	}

	// If pagination was requested, return object with rows/total
	if pageNumStr != "" || pageSizeStr != "" {
		c.JSON(http.StatusOK, gin.H{
			"rows":  roleResponses,
			"total": total,
		})
	} else {
		// No pagination params -> Return all
		c.JSON(http.StatusOK, roleResponses)
	}
}

type RoleRequest struct {
	models.SysRole
	MenuIds []string `json:"menuIds"`
}

func CreateRole(c *gin.Context) {
	var req RoleRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	tx := models.DB.Begin()
	
	// Create Role
	role := req.SysRole
	// role.ID will be generated
	if err := tx.Create(&role).Error; err != nil {
		tx.Rollback()
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	// Save Menu Associations
	if len(req.MenuIds) > 0 {
		for _, menuId := range req.MenuIds {
			mUUID, err := uuid.Parse(menuId)
			if err == nil {
				tx.Create(&models.SysRoleMenu{
					RoleID: role.ID,
					MenuID: mUUID,
				})
			}
		}
	}

	tx.Commit()
	c.JSON(http.StatusOK, role)
}

func UpdateRole(c *gin.Context) {
	id := c.Param("id")
	var role models.SysRole
	if err := models.DB.Where("id = ?", id).First(&role).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Role not found"})
		return
	}

	var req RoleRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	tx := models.DB.Begin()

	// Update Role fields
	role.Name = req.Name
	role.RoleKey = req.RoleKey
	role.DataScope = req.DataScope
	role.Status = req.Status
	role.MenuCheckStrictly = req.MenuCheckStrictly

	if err := tx.Save(&role).Error; err != nil {
		tx.Rollback()
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	// Update Menu Associations
	// First, delete existing
	tx.Delete(&models.SysRoleMenu{}, "role_id = ?", role.ID)
	
	// Then add new
	if len(req.MenuIds) > 0 {
		for _, menuId := range req.MenuIds {
			mUUID, err := uuid.Parse(menuId)
			if err == nil {
				tx.Create(&models.SysRoleMenu{
					RoleID: role.ID,
					MenuID: mUUID,
				})
			}
		}
	}

	tx.Commit()
	c.JSON(http.StatusOK, role)
}

func DeleteRole(c *gin.Context) {
	id := c.Param("id")
	tx := models.DB.Begin()
	
	// Delete associations
	tx.Delete(&models.SysUserRole{}, "role_id = ?", id)
	tx.Delete(&models.SysRoleMenu{}, "role_id = ?", id)
	
	// Delete role
	if err := tx.Delete(&models.SysRole{}, "id = ?", id).Error; err != nil {
		tx.Rollback()
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	
	tx.Commit()
	c.JSON(http.StatusOK, gin.H{"message": "Deleted"})
}

// --- Config Controller ---

// SetDebugLog 控制采集服务的调试模式
func SetDebugLog(c *gin.Context) {
	var req struct {
		Enabled bool `json:"enabled"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	if services.GlobalCollector != nil {
		services.GlobalCollector.SetDebugMode(req.Enabled)
		status := "Disabled"
		if req.Enabled {
			status = "Enabled"
		}
		c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("Collector Debug Mode %s", status), "enabled": req.Enabled})
	} else {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Collector service not initialized"})
	}
}

func GetConfig(c *gin.Context) {
	key := c.Param("key")
	var config models.SysConfig
	if err := models.DB.Where("config_key = ?", key).First(&config).Error; err != nil {
		// Return 404 or just empty
		c.JSON(http.StatusNotFound, gin.H{"error": "Config not found"})
		return
	}
	c.JSON(http.StatusOK, config)
}

func GetAllConfigs(c *gin.Context) {
	var configs []models.SysConfig
	if err := models.DB.Find(&configs).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, configs)
}

func UpdateConfig(c *gin.Context) {
	var input models.SysConfig
	if err := c.ShouldBindJSON(&input); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	var config models.SysConfig
	err := models.DB.Where("config_key = ?", input.ConfigKey).First(&config).Error
	if err == nil {
		// Update
		config.ConfigValue = input.ConfigValue
		config.Remark = input.Remark
		models.DB.Save(&config)
	} else {
		// Create
		models.DB.Create(&input)
		config = input // Update config var for check below
	}
	
	// --- 新增: 如果是采集频率配置，触发服务重启 ---
	if config.ConfigKey == "collection_frequency" && services.GlobalCollector != nil {
		if err := services.GlobalCollector.Restart(config.ConfigValue); err != nil {
			// 虽然保存成功了，但是应用失败，提示用户
			c.JSON(http.StatusOK, gin.H{
				"warning": "配置已保存，但格式错误导致立即生效失败，已回退到默认值。请检查 Cron 表达式。",
				"data":    input,
			})
			return
		}
	}

	c.JSON(http.StatusOK, input)
}

// --- User Controller (Enhanced) ---

func GetUsers(c *gin.Context) {
	var users []models.User
	if err := models.DB.Find(&users).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, users)
}

type UserRequest struct {
	models.User
	RoleIds []string `json:"roleIds"`
}

func CreateUser(c *gin.Context) {
	var req UserRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// 1. Generate Username based on Name
	// Rule: Pinyin(Surname) + FirstLetter(Name)
	// Example: 刘强 -> liuq
	baseUsername := utils.GenerateUsernameBase(req.Name)
	finalUsername := baseUsername
	
	// 2. Check for duplicates and increment
	// liuq -> liuq2 -> liuq3 ...
	suffix := 1
	for {
		var count int64
		// Check if username exists
		models.DB.Model(&models.User{}).Where("username = ?", finalUsername).Count(&count)
		if count == 0 {
			break
		}
		suffix++
		finalUsername = fmt.Sprintf("%s%d", baseUsername, suffix)
	}

	// 3. Generate Random Password
	randomPassword := utils.GenerateRandomPassword()
	
	// Check other unique fields (Email, PhoneNumber)
	// Only check if they are not empty
	var count int64
	query := models.DB.Model(&models.User{})
	
	hasCondition := false
	if req.Email != nil && *req.Email != "" {
		query = query.Where("email = ?", *req.Email)
		hasCondition = true
	}
	if req.PhoneNumber != nil && *req.PhoneNumber != "" {
		// Regex check for phone number
		matched, _ := regexp.MatchString(`^1[3-9]\d{9}$`, *req.PhoneNumber)
		if !matched {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid phone number format"})
			return
		}

		if hasCondition {
			query = models.DB.Model(&models.User{}).Where("email = ? OR phone_number = ?", *req.Email, *req.PhoneNumber)
		} else {
			query = query.Where("phone_number = ?", *req.PhoneNumber)
		}
		hasCondition = true
	}

	if hasCondition {
		query.Count(&count)
		if count > 0 {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Email or Phone Number already exists"})
			return
		}
	}

	tx := models.DB.Begin()
	user := req.User
	user.Username = finalUsername
	user.Password = randomPassword 
	
	// Handle empty strings -> nil conversion for GORM
	if user.Email != nil && *user.Email == "" {
		user.Email = nil
	}
	if user.PhoneNumber != nil && *user.PhoneNumber == "" {
		user.PhoneNumber = nil
	}
	
	// Basic validation
	if err := tx.Create(&user).Error; err != nil {
		tx.Rollback()
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	// Roles
	if len(req.RoleIds) > 0 {
		for _, rid := range req.RoleIds {
			rUUID, _ := uuid.Parse(rid)
			ur := models.SysUserRole{
				UserID: user.ID,
				RoleID: rUUID,
			}
			tx.Create(&ur)
		}
	} else {
		// No roles assigned = No permissions
		// Just don't create any SysUserRole entries
	}

	tx.Commit()
	
	// Return the generated credentials so admin can inform the user
	c.JSON(http.StatusOK, gin.H{
		"user": user,
		"generated_password": randomPassword,
	})
}

func UpdateUser(c *gin.Context) {
	id := c.Param("id")
	var user models.User
	if err := models.DB.Where("id = ?", id).First(&user).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
		return
	}

	if user.Username == "admin" {
		currentUsername, exists := c.Get("username")
		if !exists || currentUsername.(string) != "admin" {
			c.JSON(http.StatusForbidden, gin.H{"error": "Only admin can update admin user"})
			return
		}
	}

	var req UserRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// Check Uniqueness (exclude self)
	// Don't check Username here because we don't update it (it's auto-generated and fixed)
	// Only check if fields are not empty
	var count int64
	query := models.DB.Model(&models.User{}).Where("id != ?", id)
	
	hasCondition := false
	if req.Email != nil && *req.Email != "" {
		hasCondition = true
	}
	if req.PhoneNumber != nil && *req.PhoneNumber != "" {
		// Regex check for phone number
		matched, _ := regexp.MatchString(`^1[3-9]\d{9}$`, *req.PhoneNumber)
		if !matched {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid phone number format"})
			return
		}
		hasCondition = true
	}

	if hasCondition {
		condition := ""
		args := []interface{}{}
		if req.Email != nil && *req.Email != "" && req.PhoneNumber != nil && *req.PhoneNumber != "" {
			condition = "(email = ? OR phone_number = ?)"
			args = append(args, *req.Email, *req.PhoneNumber)
		} else if req.Email != nil && *req.Email != "" {
			condition = "email = ?"
			args = append(args, *req.Email)
		} else if req.PhoneNumber != nil && *req.PhoneNumber != "" {
			condition = "phone_number = ?"
			args = append(args, *req.PhoneNumber)
		}
		
		query.Where(condition, args...).Count(&count)
		if count > 0 {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Email or Phone Number already exists"})
			return
		}
	}

	tx := models.DB.Begin()

	user.Name = req.Name
	user.Email = req.Email
	user.PhoneNumber = req.PhoneNumber
	user.Status = req.Status
	// Don't update password here normally, or check if provided
	
	// Handle empty strings -> nil conversion for GORM
	if user.Email != nil && *user.Email == "" {
		user.Email = nil
	}
	if user.PhoneNumber != nil && *user.PhoneNumber == "" {
		user.PhoneNumber = nil
	}

	if err := tx.Save(&user).Error; err != nil {
		tx.Rollback()
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	// Roles
	tx.Delete(&models.SysUserRole{}, "user_id = ?", user.ID)
	// If RoleIds is empty, it just deletes old roles and adds none (user has no permissions)
	if len(req.RoleIds) > 0 {
		for _, rid := range req.RoleIds {
			rUUID, _ := uuid.Parse(rid)
			ur := models.SysUserRole{
				UserID: user.ID,
				RoleID: rUUID,
			}
			tx.Create(&ur)
		}
	}

	tx.Commit()
	c.JSON(http.StatusOK, user)
}

func DeleteUser(c *gin.Context) {
	id := c.Param("id")

	// Check if user exists and if it is admin
	var user models.User
	if err := models.DB.First(&user, "id = ?", id).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
		return
	}

	if user.Username == "admin" {
		c.JSON(http.StatusForbidden, gin.H{"error": "Cannot delete admin user"})
		return
	}

	tx := models.DB.Begin()
	if err := tx.Delete(&models.User{}, "id = ?", id).Error; err != nil {
		tx.Rollback()
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	// Cascade delete roles (soft delete if using gorm.Model, but here hard delete)
	tx.Delete(&models.SysUserRole{}, "user_id = ?", id)
	
	tx.Commit()
	c.JSON(http.StatusOK, gin.H{"message": "Deleted"})
}
