package service

import (
	"errors"
	"fmt"
	"time"

	"github.com/golang-jwt/jwt/v5"
	"github.com/gouki/lunar-server/internal/config"
	"github.com/gouki/lunar-server/internal/model"
	"gorm.io/gorm"
)

// UserService 用户服务
type UserService struct {
	db *gorm.DB
}

// NewUserService 创建用户服务
func NewUserService() *UserService {
	return &UserService{
		db: config.GetDB(),
	}
}

// GetUserByOpenID 根据OpenID获取用户
func (s *UserService) GetUserByOpenID(openID string) (*model.User, error) {
	var user model.User
	if err := s.db.Where("open_id = ?", openID).First(&user).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		return nil, err
	}
	return &user, nil
}

// GetUserByID 根据ID获取用户
func (s *UserService) GetUserByID(id uint) (*model.User, error) {
	var user model.User
	if err := s.db.First(&user, id).Error; err != nil {
		return nil, err
	}
	return &user, nil
}

// CreateUser 创建用户
func (s *UserService) CreateUser(user *model.User) error {
	return s.db.Create(user).Error
}

// UpdateUser 更新用户
func (s *UserService) UpdateUser(user *model.User) error {
	return s.db.Save(user).Error
}

// UpdateUserProfile 更新用户资料
func (s *UserService) UpdateUserProfile(profile *model.UserProfile) error {
	return s.db.Save(profile).Error
}

// GetUserProfile 获取用户资料
func (s *UserService) GetUserProfile(userID uint) (*model.UserProfile, error) {
	var profile model.UserProfile
	if err := s.db.Where("user_id = ?", userID).First(&profile).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		return nil, err
	}
	return &profile, nil
}

// ErrBirthProfileLimit 生辰档案数量达到免费上限
var ErrBirthProfileLimit = fmt.Errorf("最多可免费绑定 %d 个生辰", model.MaxFreeBirthProfiles)

// ListBirthProfiles 获取用户的全部生辰档案
func (s *UserService) ListBirthProfiles(userID uint) ([]model.BirthProfile, error) {
	var profiles []model.BirthProfile
	if err := s.db.Where("user_id = ?", userID).Order("id ASC").Find(&profiles).Error; err != nil {
		return nil, err
	}
	return profiles, nil
}

// CreateBirthProfile 新增生辰档案，超过免费上限时返回 ErrBirthProfileLimit
func (s *UserService) CreateBirthProfile(profile *model.BirthProfile) error {
	var count int64
	if err := s.db.Model(&model.BirthProfile{}).Where("user_id = ?", profile.UserID).Count(&count).Error; err != nil {
		return err
	}
	if count >= model.MaxFreeBirthProfiles {
		return ErrBirthProfileLimit
	}
	return s.db.Create(profile).Error
}

// DeleteBirthProfile 删除生辰档案（仅限本人）
func (s *UserService) DeleteBirthProfile(id, userID uint) error {
	return s.db.Where("id = ? AND user_id = ?", id, userID).Delete(&model.BirthProfile{}).Error
}

// GenerateToken 生成JWT Token
func (s *UserService) GenerateToken(userID uint, secret string) (string, error) {
	claims := jwt.MapClaims{
		"user_id": userID,
		"exp":     time.Now().Add(time.Hour * 24 * 7).Unix(), // 7天过期
		"iat":     time.Now().Unix(),
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return token.SignedString([]byte(secret))
}

// ParseToken 解析JWT Token
func (s *UserService) ParseToken(tokenString, secret string) (uint, error) {
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		return []byte(secret), nil
	})

	if err != nil {
		return 0, err
	}

	if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
		// 管理员 token 不绑定具体用户，不能当普通用户 token 使用
		if role, _ := claims["role"].(string); role == "admin" {
			return 0, errors.New("admin token")
		}
		userIDFloat, ok := claims["user_id"].(float64)
		if !ok {
			return 0, errors.New("invalid token claims")
		}
		return uint(userIDFloat), nil
	}

	return 0, errors.New("invalid token")
}

// AdminTokenCookie 管理员会话 Cookie 名
const AdminTokenCookie = "lunar_admin_token"

// GenerateAdminToken 签发管理员 JWT（12 小时过期）
func (s *UserService) GenerateAdminToken(secret string) (string, error) {
	claims := jwt.MapClaims{
		"role": "admin",
		"exp":  time.Now().Add(time.Hour * 12).Unix(),
		"iat":  time.Now().Unix(),
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return token.SignedString([]byte(secret))
}

// IsAdminToken 校验是否为有效的管理员 JWT
func (s *UserService) IsAdminToken(tokenString, secret string) bool {
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		return []byte(secret), nil
	})
	if err != nil {
		return false
	}

	claims, ok := token.Claims.(jwt.MapClaims)
	if !ok || !token.Valid {
		return false
	}
	role, _ := claims["role"].(string)
	return role == "admin"
}
