package handler

import (
	"strconv"
	"strings"
	"time"
	"xboard-go/internal/database"
	"xboard-go/internal/model"
	"xboard-go/internal/service"

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

const unverifiedExpiration = int64(946684800)

func PluginRealNameStatus(c *gin.Context) {
	user, ok := currentUser(c)
	if !ok {
		Fail(c, 401, "unauthorized")
		return
	}

	auth := realNameRecordForUser(user)
	status := "unverified"
	var realName, masked, rejectReason string
	var submittedAt, reviewedAt int64
	if auth != nil {
		status = auth.Status
		realName = auth.RealName
		masked = auth.IdentityMasked
		rejectReason = auth.RejectReason
		submittedAt = auth.SubmittedAt
		reviewedAt = auth.ReviewedAt
	}
	canSubmit := status == "unverified" || (status == "rejected" && service.GetPluginConfigBool(service.PluginRealNameVerification, "allow_resubmit_after_reject", true))

	Success(c, gin.H{
		"status":             status,
		"status_label":       realNameStatusLabel(status),
		"is_inherited":       strings.Contains(user.Email, "-ipv6@"),
		"can_submit":         canSubmit,
		"real_name":          realName,
		"identity_no_masked": masked,
		"notice":             service.GetPluginConfigString(service.PluginRealNameVerification, "verification_notice", "Please submit real-name information."),
		"reject_reason":      rejectReason,
		"submitted_at":       submittedAt,
		"reviewed_at":        reviewedAt,
	})
}

func PluginRealNameSubmit(c *gin.Context) {
	user, ok := currentUser(c)
	if !ok {
		Fail(c, 401, "unauthorized")
		return
	}

	var req struct {
		RealName   string `json:"real_name" binding:"required"`
		IdentityNo string `json:"identity_no" binding:"required"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		Fail(c, 422, "missing identity information")
		return
	}

	now := time.Now().Unix()
	record := model.RealNameAuth{
		UserID:            uint64(user.ID),
		RealName:          req.RealName,
		IdentityMasked:    maskIdentity(req.IdentityNo),
		IdentityEncrypted: req.IdentityNo,
		Status:            "pending",
		SubmittedAt:       now,
		ReviewedAt:        0,
		CreatedAt:         now,
		UpdatedAt:         now,
	}

	var existing model.RealNameAuth
	status := "pending"
	reviewedAt := int64(0)
	if service.GetPluginConfigBool(service.PluginRealNameVerification, "auto_approve", false) {
		status = "approved"
		reviewedAt = now
	}

	if err := database.DB.Where("user_id = ?", user.ID).First(&existing).Error; err == nil {
		if existing.Status == "approved" {
			Fail(c, 400, "verification already approved")
			return
		}
		if existing.Status == "pending" {
			Fail(c, 400, "verification is pending review")
			return
		}
		if existing.Status == "rejected" && !service.GetPluginConfigBool(service.PluginRealNameVerification, "allow_resubmit_after_reject", true) {
			Fail(c, 400, "rejected records cannot be resubmitted")
			return
		}

		record.ID = existing.ID
		record.CreatedAt = existing.CreatedAt
		record.Status = status
		record.ReviewedAt = reviewedAt
		record.RejectReason = ""
		database.DB.Model(&existing).Updates(record)
	} else {
		record.Status = status
		record.ReviewedAt = reviewedAt
		database.DB.Create(&record)
	}

	syncRealNameExpiration(user.ID, status)
	SuccessMessage(c, "verification submitted", gin.H{"status": status})
}

func PluginRealNameRecords(c *gin.Context) {
	page := parsePositiveInt(c.DefaultQuery("page", "1"), 1)
	perPage := parsePositiveInt(c.DefaultQuery("per_page", "20"), 20)
	keyword := strings.TrimSpace(c.Query("keyword"))

	query := database.DB.Table("v2_user AS u").
		Select("u.id, u.email, r.status, r.real_name, r.identity_masked, r.submitted_at, r.reviewed_at").
		Joins("LEFT JOIN v2_realname_auth AS r ON u.id = r.user_id").
		Where("u.email NOT LIKE ?", "%-ipv6@%")
	if keyword != "" {
		query = query.Where("u.email LIKE ?", "%"+keyword+"%")
	}

	var total int64
	query.Count(&total)

	type recordRow struct {
		ID             int
		Email          string
		Status         *string
		RealName       *string
		IdentityMasked *string
		SubmittedAt    *int64
		ReviewedAt     *int64
	}

	var rows []recordRow
	if err := query.Offset((page - 1) * perPage).Limit(perPage).Scan(&rows).Error; err != nil {
		Fail(c, 500, "failed to fetch records")
		return
	}

	items := make([]gin.H, 0, len(rows))
	for _, row := range rows {
		status := "unverified"
		if row.Status != nil && *row.Status != "" {
			status = *row.Status
		}
		items = append(items, gin.H{
			"id":                 row.ID,
			"email":              row.Email,
			"status":             status,
			"status_label":       realNameStatusLabel(status),
			"real_name":          stringPointerValue(row.RealName),
			"identity_no_masked": stringPointerValue(row.IdentityMasked),
			"submitted_at":       int64PointerValue(row.SubmittedAt),
			"reviewed_at":        int64PointerValue(row.ReviewedAt),
		})
	}

	Success(c, gin.H{
		"status": "success",
		"data":   items,
		"pagination": gin.H{
			"total":     total,
			"current":   page,
			"last_page": calculateLastPage(total, perPage),
		},
	})
}

func PluginRealNameReview(c *gin.Context) {
	userID := parsePositiveInt(c.Param("userId"), 0)
	if userID == 0 {
		Fail(c, 400, "invalid user id")
		return
	}

	var req struct {
		Status string `json:"status"`
		Reason string `json:"reason"`
	}
	_ = c.ShouldBindJSON(&req)
	if req.Status == "" {
		req.Status = "approved"
	}

	now := time.Now().Unix()
	var record model.RealNameAuth
	if err := database.DB.Where("user_id = ?", userID).First(&record).Error; err == nil {
		record.Status = req.Status
		record.RejectReason = req.Reason
		record.ReviewedAt = now
		record.UpdatedAt = now
		database.DB.Save(&record)
	} else {
		database.DB.Create(&model.RealNameAuth{
			UserID:         uint64(userID),
			Status:         req.Status,
			RealName:       "admin approved",
			IdentityMasked: "admin approved",
			SubmittedAt:    now,
			ReviewedAt:     now,
			RejectReason:   req.Reason,
			CreatedAt:      now,
			UpdatedAt:      now,
		})
	}

	syncRealNameExpiration(userID, req.Status)
	SuccessMessage(c, "review updated", true)
}

func PluginRealNameReset(c *gin.Context) {
	userID := parsePositiveInt(c.Param("userId"), 0)
	if userID == 0 {
		Fail(c, 400, "invalid user id")
		return
	}

	database.DB.Where("user_id = ?", userID).Delete(&model.RealNameAuth{})
	syncRealNameExpiration(userID, "unverified")
	SuccessMessage(c, "record reset", true)
}

func PluginRealNameSyncAll(c *gin.Context) {
	SuccessMessage(c, "sync completed", performGlobalRealNameSync())
}

func PluginRealNameApproveAll(c *gin.Context) {

	var users []model.User
	database.DB.Where("email NOT LIKE ?", "%-ipv6@%").Find(&users)
	now := time.Now().Unix()
	for _, user := range users {
		var record model.RealNameAuth
		if err := database.DB.Where("user_id = ?", user.ID).First(&record).Error; err == nil {
			record.Status = "approved"
			record.RealName = "admin approved"
			record.IdentityMasked = "admin approved"
			record.SubmittedAt = now
			record.ReviewedAt = now
			record.UpdatedAt = now
			database.DB.Save(&record)
		} else {
			database.DB.Create(&model.RealNameAuth{
				UserID:         uint64(user.ID),
				Status:         "approved",
				RealName:       "admin approved",
				IdentityMasked: "admin approved",
				SubmittedAt:    now,
				ReviewedAt:     now,
				CreatedAt:      now,
				UpdatedAt:      now,
			})
		}
	}

	SuccessMessage(c, "all users approved", performGlobalRealNameSync())
}

func PluginRealNameClearCache(c *gin.Context) {
	_ = database.CacheDelete("realname:sync")
	SuccessMessage(c, "cache cleared", true)
}

func realNameRecordForUser(user *model.User) *model.RealNameAuth {
	if user == nil {
		return nil
	}
	var record model.RealNameAuth
	if err := database.DB.Where("user_id = ?", user.ID).First(&record).Error; err == nil {
		return &record
	}

	if strings.Contains(user.Email, "-ipv6@") {
		mainEmail := strings.Replace(user.Email, "-ipv6@", "@", 1)
		var mainUser model.User
		if err := database.DB.Where("email = ?", mainEmail).First(&mainUser).Error; err == nil {
			if err := database.DB.Where("user_id = ?", mainUser.ID).First(&record).Error; err == nil {
				return &record
			}
		}
	}

	return nil
}

func syncRealNameExpiration(userID int, status string) {
	if status == "approved" {
		database.DB.Model(&model.User{}).Where("id = ?", userID).Updates(map[string]any{
			"expired_at": parseExpirationSetting(),
			"updated_at": time.Now().Unix(),
		})
		return
	}
	if !service.GetPluginConfigBool(service.PluginRealNameVerification, "enforce_real_name", false) {
		database.DB.Model(&model.User{}).Where("id = ?", userID).Update("updated_at", time.Now().Unix())
		return
	}
	database.DB.Model(&model.User{}).Where("id = ?", userID).Updates(map[string]any{
		"expired_at": unverifiedExpiration,
		"updated_at": time.Now().Unix(),
	})
}

func parseExpirationSetting() int64 {
	raw := service.GetPluginConfigString(service.PluginRealNameVerification, "verified_expiration_date", "2099-12-31")
	if value, err := strconv.ParseInt(raw, 10, 64); err == nil {
		return value
	}
	if parsed, err := time.Parse("2006-01-02", raw); err == nil {
		return parsed.Unix()
	}
	return time.Date(2099, 12, 31, 23, 59, 59, 0, time.UTC).Unix()
}

func performGlobalRealNameSync() gin.H {
	expireApproved := parseExpirationSetting()
	now := time.Now().Unix()
	enforce := service.GetPluginConfigBool(service.PluginRealNameVerification, "enforce_real_name", false)

	type approvedRow struct {
		UserID int
	}
	var approvedRows []approvedRow
	database.DB.Table("v2_realname_auth").Select("user_id").Where("status = ?", "approved").Scan(&approvedRows)
	approvedUsers := make([]int, 0, len(approvedRows))
	for _, row := range approvedRows {
		approvedUsers = append(approvedUsers, row.UserID)
	}

	if len(approvedUsers) > 0 {
		database.DB.Model(&model.User{}).Where("id IN ?", approvedUsers).
			Updates(map[string]any{"expired_at": expireApproved, "updated_at": now})
		if enforce {
			database.DB.Model(&model.User{}).Where("id NOT IN ?", approvedUsers).
				Updates(map[string]any{"expired_at": unverifiedExpiration, "updated_at": now})
		}
	} else {
		if enforce {
			database.DB.Model(&model.User{}).
				Updates(map[string]any{"expired_at": unverifiedExpiration, "updated_at": now})
		}
	}

	return gin.H{
		"enforce_real_name": enforce,
		"total_verified":    len(approvedUsers),
		"actual_synced":     len(approvedUsers),
	}
}

func realNameStatusLabel(status string) string {
	switch status {
	case "approved":
		return "approved"
	case "pending":
		return "pending"
	case "rejected":
		return "rejected"
	default:
		return "unverified"
	}
}

func maskIdentity(identity string) string {
	if len(identity) <= 8 {
		return identity
	}
	return identity[:4] + "**********" + identity[len(identity)-4:]
}

func stringPointerValue(value *string) string {
	if value == nil {
		return ""
	}
	return *value
}

func int64PointerValue(value *int64) int64 {
	if value == nil {
		return 0
	}
	return *value
}
