package commerce

import (
	"context"
	"errors"

	"gorm.io/gorm"
)

func (s *Service) adminUserDetail(ctx context.Context, user UserAccount) (map[string]any, error) {
	data := adminUserMap(user)
	data["has_password"] = user.HashedPassword != nil && *user.HashedPassword != ""
	data["id_card_verified"] = user.IDCardVerified
	data["id_card_front_image"] = user.IDCardFrontImage
	data["id_card_back_image"] = user.IDCardBackImage
	data["id_card_face_image"] = user.IDCardFaceImage
	data["id_card_verify_remark"] = user.IDCardVerifyRemark
	data["full_refund_quota_extra"] = user.FullRefundQuotaExtra
	data["full_refund_quota_reset_at"] = user.FullRefundQuotaResetAt
	if s.identity != nil {
		realName := s.identity.DecryptSensitiveOptional(user.RealName)
		idCardNo := s.identity.DecryptSensitiveOptional(user.IDCardNo)
		data["real_name"] = realName
		data["real_name_full"] = realName
		data["id_card_no"] = idCardNo
		data["id_card_no_full"] = idCardNo
	}

	membership, err := s.adminUserMembership(ctx, user.ID)
	if err != nil {
		return nil, err
	}
	data["membership"] = membership

	baseRefundLimit := s.settingInt(ctx, "refund.full_refund_limit", 3)
	refundUsed, err := s.adminFullRefundCount(ctx, user)
	if err != nil {
		return nil, err
	}
	var activeAgents int64
	if err := s.db.WithContext(ctx).Model(&UserAgent{}).Where("user_id = ? AND status = ?", user.ID, "active").Count(&activeAgents).Error; err != nil {
		return nil, err
	}
	refundLimit := baseRefundLimit + maxInt(0, user.FullRefundQuotaExtra)
	remaining := maxInt(0, refundLimit-refundUsed)
	data["refund_quota"] = map[string]any{
		"base_limit": baseRefundLimit, "extra": maxInt(0, user.FullRefundQuotaExtra), "limit": refundLimit,
		"used": refundUsed, "remaining": remaining, "unlimited": activeAgents > 0, "reset_at": user.FullRefundQuotaResetAt,
	}
	if activeAgents > 0 {
		data["refund_quota"].(map[string]any)["remaining"] = nil
	}

	inviteLimit := inviteCodeChangeLimit(user)
	inviteUsed := maxInt(0, user.InviteCodeChangeCount)
	data["invite_code_quota"] = map[string]any{
		"current_code": user.InviteCode, "base_limit": 3, "extra": maxInt(0, user.InviteCodeChangeExtra),
		"limit": inviteLimit, "used": inviteUsed, "remaining": maxInt(0, inviteLimit-inviteUsed),
	}
	inviteCodes, err := rawRows(s.db.WithContext(ctx).Table("user_invite_codes").Where("user_id = ?", user.ID).Order("is_current DESC, id DESC"))
	if err != nil {
		return nil, err
	}
	data["invite_codes"] = inviteCodes
	inviteLogs, err := rawRows(s.db.WithContext(ctx).Table("user_invite_code_logs").Where("user_id = ?", user.ID).Order("id DESC").Limit(50))
	if err != nil {
		return nil, err
	}
	data["invite_code_logs"] = inviteLogs

	quotaRows, err := rawRows(s.db.WithContext(ctx).Table("user_verification_quotas").Where("user_id = ?", user.ID).Limit(1))
	if err != nil {
		return nil, err
	}
	freeUsed, paidRemaining, paidPurchased, paidUsed := 0, 0, 0, 0
	if len(quotaRows) > 0 {
		freeUsed = intValueAny(quotaRows[0]["free_used"], 0)
		paidRemaining = intValueAny(quotaRows[0]["paid_remaining"], 0)
		paidPurchased = intValueAny(quotaRows[0]["paid_purchased"], 0)
		paidUsed = intValueAny(quotaRows[0]["paid_used"], 0)
	}
	freeLimit := s.settingInt(ctx, "verification.free_quota_per_user", 1)
	price := s.settingInt(ctx, "verification.quota_price_cents", 350)
	purchaseEnabled := s.settingBool(ctx, "verification.quota_purchase_enabled", true)
	freeRemaining := maxInt(0, freeLimit-freeUsed)
	remainingTotal := freeRemaining + maxInt(0, paidRemaining)
	data["verification_quota"] = map[string]any{
		"free_quota_per_user": freeLimit, "purchase_enabled": purchaseEnabled, "price_cents": price,
		"free_used": maxInt(0, freeUsed), "free_remaining": freeRemaining, "paid_remaining": maxInt(0, paidRemaining),
		"paid_purchased": maxInt(0, paidPurchased), "paid_used": maxInt(0, paidUsed), "remaining_total": remainingTotal,
		"balance_cents": maxInt(0, user.Balance), "can_submit": remainingTotal > 0, "can_purchase": purchaseEnabled && price >= 0,
	}
	verificationRecords, err := rawRows(s.db.WithContext(ctx).Table("user_verification_records").Where("user_id = ?", user.ID).Order("id DESC").Limit(20))
	if err != nil {
		return nil, err
	}
	if s.identity != nil {
		for _, record := range verificationRecords {
			if value, ok := record["real_name"].(string); ok {
				record["real_name"] = s.identity.DecryptSensitiveOptional(&value)
			}
			if value, ok := record["id_card_no"].(string); ok {
				record["id_card_no"] = s.identity.DecryptSensitiveOptional(&value)
			}
		}
	}
	data["verification_records"] = verificationRecords

	var tickets []Ticket
	if err := s.db.WithContext(ctx).Where("user_id = ?", user.ID).Order("last_message_at DESC, id DESC").Limit(10).Find(&tickets).Error; err != nil {
		return nil, err
	}
	ticketItems := make([]map[string]any, 0, len(tickets))
	for _, ticket := range tickets {
		ticketItems = append(ticketItems, map[string]any{
			"id": ticket.ID, "ticket_no": ticket.TicketNo, "subject": ticket.Subject, "status": ticket.Status,
			"priority": ticket.Priority, "department_name": ticket.DepartmentName, "instance_id": ticket.InstanceID,
			"assignee_id": ticket.AssigneeID, "admin_unread_count": ticket.AdminUnreadCount,
			"user_unread_count": ticket.UserUnreadCount, "satisfaction_rating": ticket.SatisfactionRating,
			"last_message_at": ticket.LastMessageAt, "closed_at": ticket.ClosedAt, "created_at": ticket.CreatedAt,
		})
	}
	data["tickets"] = ticketItems

	stats, err := s.adminUserDetailStats(ctx, user.ID)
	if err != nil {
		return nil, err
	}
	data["stats"] = stats
	return data, nil
}

func (s *Service) adminUserMembership(ctx context.Context, userID int64) (map[string]any, error) {
	membership, err := s.getOrCreateMembership(ctx, userID)
	if err != nil {
		return nil, err
	}
	data := mapFromJSON(membership)
	data["member_level"] = nil
	if membership.MemberLevelID != nil {
		var level MemberLevel
		if err := s.db.WithContext(ctx).Where("id = ?", *membership.MemberLevelID).First(&level).Error; err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, err
		} else if err == nil {
			data["member_level"] = mapFromJSON(level)
		}
	}
	return data, nil
}

func (s *Service) adminFullRefundCount(ctx context.Context, user UserAccount) (int, error) {
	db := s.db.WithContext(ctx).Table("order_refunds AS r").Joins("JOIN orders AS o ON o.id = r.order_id").
		Where("r.user_id = ? AND r.refund_type = ? AND r.status = ? AND r.refund_amount > 0", user.ID, "full", "success")
	if user.FullRefundQuotaResetAt != nil {
		db = db.Where("r.created_at >= ?", *user.FullRefundQuotaResetAt)
	}
	var count int64
	if err := db.Count(&count).Error; err != nil {
		return 0, err
	}
	return int(count), nil
}

func (s *Service) adminUserDetailStats(ctx context.Context, userID int64) (map[string]any, error) {
	stats := map[string]any{}
	counts := []struct {
		key string
		db  *gorm.DB
	}{
		{"instances_total", s.db.WithContext(ctx).Model(&Instance{}).Where("user_id = ?", userID)},
		{"instances_active", s.db.WithContext(ctx).Model(&Instance{}).Where("user_id = ? AND status = ?", userID, "active")},
		{"instances_suspended", s.db.WithContext(ctx).Model(&Instance{}).Where("user_id = ? AND status = ?", userID, "suspended")},
		{"orders_total", s.db.WithContext(ctx).Model(&Order{}).Where("user_id = ?", userID)},
		{"balance_logs_total", s.db.WithContext(ctx).Model(&BalanceLog{}).Where("user_id = ?", userID)},
		{"verification_records_total", s.db.WithContext(ctx).Table("user_verification_records").Where("user_id = ?", userID)},
		{"tickets_total", s.db.WithContext(ctx).Model(&Ticket{}).Where("user_id = ?", userID)},
		{"tickets_open", s.db.WithContext(ctx).Model(&Ticket{}).Where("user_id = ? AND status IN ?", userID, []string{"open", "pending", "processing", "waiting_user", "answered"})},
	}
	for _, item := range counts {
		var count int64
		if err := item.db.Count(&count).Error; err != nil {
			return nil, err
		}
		stats[item.key] = count
	}
	var paidAmount int64
	if err := s.db.WithContext(ctx).Model(&Order{}).Where("user_id = ?", userID).Select("COALESCE(SUM(total_amount - discount_amount), 0)").Scan(&paidAmount).Error; err != nil {
		return nil, err
	}
	stats["orders_paid_amount"] = paidAmount
	var unread int64
	if err := s.db.WithContext(ctx).Model(&Ticket{}).Where("user_id = ?", userID).Select("COALESCE(SUM(admin_unread_count), 0)").Scan(&unread).Error; err != nil {
		return nil, err
	}
	stats["tickets_unread"] = unread
	return stats, nil
}
