package services

import (
	"context"
	"encoding/json"
	"errors"
	"time"

	"quyun/v2/app/errorx"
	auth_dto "quyun/v2/app/http/v1/dto"
	user_dto "quyun/v2/app/http/v1/dto"
	"quyun/v2/database/models"
	"quyun/v2/pkg/consts"
	"quyun/v2/providers/jwt"

	"go.ipao.vip/gen/types"
	"gorm.io/gorm"
)

// @provider
type user struct {
	jwt *jwt.JWT
}

// SendOTP 发送验证码
// 当前仅模拟发送，实际应对接短信服务
func (s *user) SendOTP(ctx context.Context, phone string) error {
	// TODO: 对接短信服务
	// 模拟发送成功
	return nil
}

// LoginWithOTP 手机号验证码登录/注册
func (s *user) LoginWithOTP(ctx context.Context, tenantID int64, phone, otp string) (*auth_dto.LoginResponse, error) {
	// 1. 校验验证码 (模拟：固定 123456)
	if otp != "1234" {
		return nil, errorx.ErrInvalidCredentials.WithMsg("验证码错误")
	}

	// 2. 查询或创建用户
	tbl, query := models.UserQuery.QueryContext(ctx)
	u, err := query.Where(tbl.Phone.Eq(phone)).First()
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			// 创建新用户
			u = &models.User{
				Phone:    phone,
				Username: phone, // 默认用户名 = 手机号
				Password: "",    // 免密登录
				Nickname: "User_" + phone[len(phone)-4:],
				Status:   consts.UserStatusVerified, // 默认已审核
				Roles:    types.Array[consts.Role]{consts.RoleUser},
				Gender:   consts.GenderSecret, // 默认性别
			}
			if err := query.Create(u); err != nil {
				return nil, errorx.ErrDatabaseError.WithCause(err).WithMsg("创建用户失败")
			}
		} else {
			return nil, errorx.ErrDatabaseError.WithCause(err).WithMsg("查询用户失败")
		}
	}

	// 3. 检查状态
	if u.Status == consts.UserStatusBanned {
		return nil, errorx.ErrAccountDisabled
	}
	// 4. 校验租户成员关系（租户上下文下仅允许成员登录）。
	if err := s.ensureTenantMember(ctx, tenantID, u.ID); err != nil {
		return nil, err
	}

	// 5. 生成 Token
	token, err := s.jwt.CreateToken(s.jwt.CreateClaims(jwt.BaseClaims{
		UserID:   u.ID,
		TenantID: tenantID,
	}))
	if err != nil {
		return nil, errorx.ErrInternalError.WithMsg("生成令牌失败")
	}

	return &auth_dto.LoginResponse{
		Token: token,
		User:  s.ToAuthUserDTO(u),
	}, nil
}

func (s *user) ensureTenantMember(ctx context.Context, tenantID, userID int64) error {
	if tenantID <= 0 {
		return nil
	}
	// 校验租户存在，避免非法租户ID绕过校验。
	tblTenant, tenantQuery := models.TenantQuery.QueryContext(ctx)
	tenant, err := tenantQuery.Where(tblTenant.ID.Eq(tenantID)).First()
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return errorx.ErrRecordNotFound.WithCause(err).WithMsg("租户不存在")
		}
		return errorx.ErrDatabaseError.WithCause(err)
	}
	if tenant.UserID == userID {
		return nil
	}

	tbl, q := models.TenantUserQuery.QueryContext(ctx)
	exists, err := q.Where(
		tbl.TenantID.Eq(tenantID),
		tbl.UserID.Eq(userID),
		tbl.Status.Eq(consts.UserStatusVerified),
	).Exists()
	if err != nil {
		return errorx.ErrDatabaseError.WithCause(err)
	}
	if !exists {
		return errorx.ErrForbidden.WithMsg("未加入该租户")
	}
	return nil
}

// GetModelByID 获取指定 ID 的用户model
func (s *user) GetModelByID(ctx context.Context, userID int64) (*models.User, error) {
	tbl, query := models.UserQuery.QueryContext(ctx)
	u, err := query.Where(tbl.ID.Eq(userID)).First()
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errorx.ErrRecordNotFound
		}
		return nil, errorx.ErrDatabaseError.WithCause(err)
	}
	return u, nil
}

// Me 获取当前用户信息
func (s *user) Me(ctx context.Context, userID int64) (*auth_dto.User, error) {
	u, err := s.GetModelByID(ctx, userID)
	if err != nil {
		return nil, err
	}
	return s.ToAuthUserDTO(u), nil
}

// Update 更新用户信息
func (s *user) Update(ctx context.Context, userID int64, form *user_dto.UserUpdate) error {
	tbl, query := models.UserQuery.QueryContext(ctx)
	_, err := query.Where(tbl.ID.Eq(userID)).Updates(&models.User{
		Nickname: form.Nickname,
		Avatar:   form.Avatar,
		Gender:   form.Gender,
		Bio:      form.Bio,
		// Birthday: form.Birthday, // 类型转换需处理
	})
	if err != nil {
		return errorx.ErrDatabaseError.WithCause(err)
	}

	return nil
}

// RealName 实名认证
func (s *user) RealName(ctx context.Context, userID int64, form *user_dto.RealNameForm) error {
	// Mock Verification
	if len(form.IDCard) != 18 {
		return errorx.ErrBadRequest.WithMsg("身份证号格式错误")
	}
	if form.Realname == "" {
		return errorx.ErrBadRequest.WithMsg("真实姓名不能为空")
	}

	tbl, query := models.UserQuery.QueryContext(ctx)
	u, err := query.Where(tbl.ID.Eq(userID)).First()
	if err != nil {
		return errorx.ErrRecordNotFound
	}

	var metaMap map[string]interface{}
	if len(u.Metas) > 0 {
		_ = json.Unmarshal(u.Metas, &metaMap)
	}
	if metaMap == nil {
		metaMap = make(map[string]interface{})
	}
	// Mock encryption
	metaMap["real_name"] = form.Realname
	metaMap["id_card"] = "ENC:" + form.IDCard

	b, _ := json.Marshal(metaMap)

	_, err = query.Where(tbl.ID.Eq(userID)).Updates(&models.User{
		IsRealNameVerified: true,
		VerifiedAt:         time.Now(),
		Metas:              types.JSON(b),
	})
	if err != nil {
		return errorx.ErrDatabaseError.WithCause(err)
	}
	return nil
}

// GetNotifications 获取通知
func (s *user) GetNotifications(ctx context.Context, tenantID, userID int64, typeArg string) ([]user_dto.Notification, error) {
	tbl, query := models.NotificationQuery.QueryContext(ctx)
	query = query.Where(tbl.UserID.Eq(userID))
	if tenantID > 0 {
		query = query.Where(tbl.TenantID.Eq(tenantID))
	}
	if typeArg != "" && typeArg != "all" {
		query = query.Where(tbl.Type.Eq(typeArg))
	}

	list, err := query.Order(tbl.CreatedAt.Desc()).Find()
	if err != nil {
		return nil, errorx.ErrDatabaseError.WithCause(err)
	}

	result := make([]user_dto.Notification, len(list))
	for i, v := range list {
		result[i] = user_dto.Notification{
			ID:      v.ID,
			Type:    v.Type,
			Title:   v.Title,
			Content: v.Content,
			Read:    v.IsRead,
			Time:    v.CreatedAt.Format(time.RFC3339),
		}
	}
	return result, nil
}

func (s *user) ToAuthUserDTO(u *models.User) *auth_dto.User {
	return &auth_dto.User{
		ID:                 u.ID,
		Phone:              u.Phone,
		Nickname:           u.Nickname,
		Avatar:             u.Avatar,
		Gender:             u.Gender, // Direct assignment, types match
		Bio:                u.Bio,
		Balance:            float64(u.Balance) / 100.0,
		Points:             u.Points,
		IsRealNameVerified: u.IsRealNameVerified,
	}
}
