package hostorg

import (
	"context"
	"fmt"
	"strconv"
	"strings"

	"hyapp-admin-server/internal/appctx"
	"hyapp-admin-server/internal/integration/userclient"
	"hyapp-admin-server/internal/integration/walletclient"
	walletv1 "hyapp.local/api/proto/wallet/v1"
)

const (
	coinSellerStatusActive        = "active"
	coinSellerMerchantAssetType   = "COIN_SELLER_COIN"
	coinSellerStockTypePurchase   = "usdt_purchase"
	coinSellerStockTypeCompensate = "coin_compensation"
	paidCurrencyUSDT              = "USDT"
)

type Service struct {
	userClient   userclient.Client
	walletClient walletclient.Client
	reader       *Reader
}

func NewService(userClient userclient.Client, walletClient walletclient.Client, reader *Reader) *Service {
	return &Service{userClient: userClient, walletClient: walletClient, reader: reader}
}

func (s *Service) ListBDLeaders(ctx context.Context, query listQuery) ([]*userclient.BDProfile, int64, error) {
	query = normalizeListQuery(query)
	return s.reader.ListBDProfiles(ctx, query, "bd_leader")
}

func (s *Service) ListBDs(ctx context.Context, query listQuery) ([]*userclient.BDProfile, int64, error) {
	query = normalizeListQuery(query)
	return s.reader.ListBDProfiles(ctx, query, "bd")
}

func (s *Service) ListAgencies(ctx context.Context, query listQuery) ([]*userclient.Agency, int64, error) {
	query = normalizeListQuery(query)
	return s.reader.ListAgencies(ctx, query)
}

func (s *Service) ListHosts(ctx context.Context, query listQuery) ([]*userclient.HostProfile, int64, error) {
	query = normalizeListQuery(query)
	return s.reader.ListHostProfiles(ctx, query)
}

func (s *Service) ListCoinSellers(ctx context.Context, query listQuery) ([]*CoinSellerListItem, int64, error) {
	query = normalizeListQuery(query)
	return s.reader.ListCoinSellers(ctx, query)
}

func (s *Service) CreateBDLeader(ctx context.Context, actorID int64, requestID string, req createBDLeaderRequest) (*userclient.BDProfile, error) {
	targetUserID, err := s.resolveDisplayUserID(ctx, requestID, req.TargetUserID)
	if err != nil {
		return nil, err
	}
	// BD Leader 创建时后台选择的区域是显式运营归属，由 user-service 在同一事务里更新 users.region_id 和 bd_profiles.region_id。
	return s.userClient.CreateBDLeader(ctx, userclient.CreateBDLeaderRequest{
		RequestID:    requestID,
		Caller:       "hyapp-admin-server",
		CommandID:    strings.TrimSpace(req.CommandID),
		AdminUserID:  actorID,
		RegionID:     req.RegionID,
		TargetUserID: targetUserID,
		Reason:       strings.TrimSpace(req.Reason),
	})
}

func (s *Service) CreateBD(ctx context.Context, actorID int64, requestID string, req createBDRequest) (*userclient.BDProfile, error) {
	targetUserID, err := s.resolveDisplayUserID(ctx, requestID, req.TargetUserID)
	if err != nil {
		return nil, err
	}
	parentLeaderUserID, err := s.resolveDisplayUserID(ctx, requestID, req.ParentLeaderUserID)
	if err != nil {
		return nil, err
	}
	return s.userClient.CreateBD(ctx, userclient.CreateBDRequest{
		RequestID:          requestID,
		Caller:             "hyapp-admin-server",
		CommandID:          strings.TrimSpace(req.CommandID),
		AdminUserID:        actorID,
		TargetUserID:       targetUserID,
		ParentLeaderUserID: parentLeaderUserID,
		Reason:             strings.TrimSpace(req.Reason),
	})
}

func (s *Service) SetBDStatus(ctx context.Context, actorID int64, targetUserID int64, requestID string, req bdStatusRequest) (*userclient.BDProfile, error) {
	return s.userClient.SetBDStatus(ctx, userclient.SetBDStatusRequest{
		RequestID:    requestID,
		Caller:       "hyapp-admin-server",
		CommandID:    strings.TrimSpace(req.CommandID),
		AdminUserID:  actorID,
		TargetUserID: targetUserID,
		Status:       strings.TrimSpace(req.Status),
		Reason:       strings.TrimSpace(req.Reason),
	})
}

func (s *Service) DeleteBDLeader(ctx context.Context, targetUserID int64) (*userclient.BDProfile, error) {
	if targetUserID <= 0 {
		return nil, fmt.Errorf("target_user_id is required")
	}
	return s.reader.DeleteBDLeader(ctx, targetUserID)
}

func (s *Service) CreateCoinSeller(ctx context.Context, actorID int64, requestID string, req createCoinSellerRequest) (*userclient.CoinSellerProfile, error) {
	targetUserID, err := s.resolveDisplayUserID(ctx, requestID, req.TargetUserID)
	if err != nil {
		return nil, err
	}
	profile, err := s.userClient.CreateCoinSeller(ctx, userclient.CreateCoinSellerRequest{
		RequestID:    requestID,
		Caller:       "hyapp-admin-server",
		CommandID:    strings.TrimSpace(req.CommandID),
		AdminUserID:  actorID,
		TargetUserID: targetUserID,
		Reason:       strings.TrimSpace(req.Reason),
	})
	if err != nil {
		return nil, err
	}
	contact := firstNonBlank(req.Contact, req.Reason)
	if contact != "" {
		if err := s.reader.UpdateCoinSellerContact(ctx, targetUserID, contact); err != nil {
			return nil, err
		}
	}
	return profile, nil
}

func (s *Service) SetCoinSellerStatus(ctx context.Context, actorID int64, targetUserID int64, requestID string, req coinSellerStatusRequest) (*userclient.CoinSellerProfile, error) {
	profile, err := s.userClient.SetCoinSellerStatus(ctx, userclient.SetCoinSellerStatusRequest{
		RequestID:    requestID,
		Caller:       "hyapp-admin-server",
		CommandID:    strings.TrimSpace(req.CommandID),
		AdminUserID:  actorID,
		TargetUserID: targetUserID,
		Status:       strings.TrimSpace(req.Status),
		Reason:       strings.TrimSpace(req.Reason),
	})
	if err != nil {
		return nil, err
	}
	if req.Contact != nil {
		if err := s.reader.UpdateCoinSellerContact(ctx, targetUserID, *req.Contact); err != nil {
			return nil, err
		}
	}
	return profile, nil
}

func (s *Service) CreditCoinSellerStock(ctx context.Context, actorID int64, sellerUserID int64, requestID string, req coinSellerStockCreditRequest) (*walletv1.AdminCreditCoinSellerStockResponse, error) {
	if s.walletClient == nil {
		return nil, fmt.Errorf("wallet client is not configured")
	}
	if sellerUserID <= 0 || actorID <= 0 {
		return nil, fmt.Errorf("seller_user_id and operator_user_id are required")
	}
	commandID := strings.TrimSpace(req.CommandID)
	stockType := strings.ToLower(strings.TrimSpace(req.Type))
	evidenceRef := strings.TrimSpace(req.EvidenceRef)
	reason := strings.TrimSpace(req.Reason)
	if commandID == "" || req.CoinAmount <= 0 {
		return nil, fmt.Errorf("command_id and coin_amount are required")
	}

	paidCurrencyCode := ""
	paidAmountMicro := int64(0)
	paymentRef := strings.TrimSpace(req.PaymentRef)
	switch stockType {
	case coinSellerStockTypePurchase:
		paidCurrencyCode = paidCurrencyUSDT
		parsed, err := parseUSDTAmountMicro(req.RechargeAmount)
		if err != nil {
			return nil, err
		}
		paidAmountMicro = parsed
	case coinSellerStockTypeCompensate:
		if strings.TrimSpace(req.RechargeAmount) != "" || paymentRef != "" {
			return nil, fmt.Errorf("coin compensation must not include recharge_amount or payment_ref")
		}
	default:
		return nil, fmt.Errorf("stock type is invalid")
	}

	profile, err := s.userClient.GetCoinSellerProfile(ctx, userclient.GetCoinSellerProfileRequest{
		RequestID: requestID,
		Caller:    "hyapp-admin-server",
		UserID:    sellerUserID,
	})
	if err != nil {
		return nil, err
	}
	if profile == nil || profile.Status != coinSellerStatusActive || profile.MerchantAssetType != coinSellerMerchantAssetType {
		return nil, fmt.Errorf("target user is not an active coin seller")
	}

	return s.walletClient.AdminCreditCoinSellerStock(ctx, &walletv1.AdminCreditCoinSellerStockRequest{
		CommandId:        commandID,
		SellerUserId:     sellerUserID,
		StockType:        stockType,
		CoinAmount:       req.CoinAmount,
		PaidCurrencyCode: paidCurrencyCode,
		PaidAmountMicro:  paidAmountMicro,
		PaymentRef:       paymentRef,
		EvidenceRef:      evidenceRef,
		OperatorUserId:   actorID,
		Reason:           reason,
		AppCode:          appctx.FromContext(ctx),
	})
}

func (s *Service) CreateAgency(ctx context.Context, actorID int64, requestID string, req createAgencyRequest) (*userclient.CreateAgencyResult, error) {
	ownerUserID, err := s.resolveDisplayUserID(ctx, requestID, req.OwnerUserID)
	if err != nil {
		return nil, err
	}
	parentBDUserID, err := s.resolveDisplayUserID(ctx, requestID, req.ParentBDUserID)
	if err != nil {
		return nil, err
	}
	joinEnabled := false
	if req.JoinEnabled != nil {
		joinEnabled = *req.JoinEnabled
	}
	return s.userClient.CreateAgency(ctx, userclient.CreateAgencyRequest{
		RequestID:      requestID,
		Caller:         "hyapp-admin-server",
		CommandID:      strings.TrimSpace(req.CommandID),
		AdminUserID:    actorID,
		OwnerUserID:    ownerUserID,
		ParentBDUserID: parentBDUserID,
		Name:           strings.TrimSpace(req.Name),
		JoinEnabled:    joinEnabled,
		MaxHosts:       req.MaxHosts,
		Reason:         strings.TrimSpace(req.Reason),
	})
}

func (s *Service) CloseAgency(ctx context.Context, actorID int64, agencyID int64, requestID string, req agencyCloseRequest) (*userclient.Agency, error) {
	return s.userClient.CloseAgency(ctx, userclient.CloseAgencyRequest{
		RequestID:   requestID,
		Caller:      "hyapp-admin-server",
		CommandID:   strings.TrimSpace(req.CommandID),
		AdminUserID: actorID,
		AgencyID:    agencyID,
		Reason:      strings.TrimSpace(req.Reason),
	})
}

func (s *Service) resolveDisplayUserID(ctx context.Context, requestID string, displayUserID int64) (int64, error) {
	if displayUserID <= 0 {
		return 0, fmt.Errorf("display_user_id is required")
	}
	identity, err := s.userClient.ResolveDisplayUserID(ctx, userclient.ResolveDisplayUserIDRequest{
		RequestID:     requestID,
		Caller:        "hyapp-admin-server",
		DisplayUserID: strconv.FormatInt(displayUserID, 10),
	})
	if err != nil {
		return 0, err
	}
	if identity == nil || identity.UserID <= 0 {
		return 0, fmt.Errorf("display_user_id %d not found", displayUserID)
	}
	return identity.UserID, nil
}

func (s *Service) SetAgencyJoinEnabled(ctx context.Context, actorID int64, agencyID int64, requestID string, req agencyJoinEnabledRequest) (*userclient.Agency, error) {
	joinEnabled := false
	if req.JoinEnabled != nil {
		joinEnabled = *req.JoinEnabled
	}
	return s.userClient.SetAgencyJoinEnabled(ctx, userclient.SetAgencyJoinEnabledRequest{
		RequestID:   requestID,
		Caller:      "hyapp-admin-server",
		CommandID:   strings.TrimSpace(req.CommandID),
		AdminUserID: actorID,
		AgencyID:    agencyID,
		JoinEnabled: joinEnabled,
		Reason:      strings.TrimSpace(req.Reason),
	})
}

func parseUSDTAmountMicro(raw string) (int64, error) {
	raw = strings.TrimSpace(raw)
	if raw == "" {
		return 0, fmt.Errorf("recharge_amount is required for usdt purchase")
	}
	parts := strings.Split(raw, ".")
	if len(parts) > 2 || parts[0] == "" {
		return 0, fmt.Errorf("recharge_amount is invalid")
	}
	whole := parts[0]
	frac := ""
	if len(parts) == 2 {
		frac = parts[1]
		if frac == "" || len(frac) > 6 {
			return 0, fmt.Errorf("recharge_amount supports up to 6 decimals")
		}
	}
	digits := whole + frac + strings.Repeat("0", 6-len(frac))
	var amount int64
	const maxInt64 = int64(^uint64(0) >> 1)
	for _, char := range digits {
		if char < '0' || char > '9' {
			return 0, fmt.Errorf("recharge_amount is invalid")
		}
		next := int64(char - '0')
		if amount > (maxInt64-next)/10 {
			return 0, fmt.Errorf("recharge_amount is too large")
		}
		amount = amount*10 + next
	}
	if amount <= 0 {
		return 0, fmt.Errorf("recharge_amount must be positive")
	}
	return amount, nil
}

func normalizeListQuery(query listQuery) listQuery {
	if query.Page < 1 {
		query.Page = 1
	}
	if query.PageSize < 1 {
		query.PageSize = 20
	}
	if query.PageSize > 100 {
		query.PageSize = 100
	}
	query.Keyword = strings.TrimSpace(query.Keyword)
	query.Status = strings.ToLower(strings.TrimSpace(query.Status))
	return query
}

func firstNonBlank(values ...string) string {
	for _, value := range values {
		if trimmed := strings.TrimSpace(value); trimmed != "" {
			return trimmed
		}
	}
	return ""
}
