package arifpay

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"strings"
	"time"

	"Yimaru-Backend/internal/config"
	"Yimaru-Backend/internal/domain"
	"Yimaru-Backend/internal/ports"

	"github.com/google/uuid"
)

var (
	ErrPaymentNotFound     = errors.New("payment not found")
	ErrPaymentAlreadyPaid  = errors.New("payment already processed")
	ErrPaymentExpired      = errors.New("payment has expired")
	ErrInvalidPaymentState = errors.New("invalid payment state")
)

type ArifpayService struct {
	cfg               *config.Config
	httpClient        *http.Client
	paymentStore      ports.PaymentStore
	subscriptionStore ports.SubscriptionStore
}

func NewArifpayService(
	cfg *config.Config,
	httpClient *http.Client,
	paymentStore ports.PaymentStore,
	subscriptionStore ports.SubscriptionStore,
) *ArifpayService {
	return &ArifpayService{
		cfg:               cfg,
		httpClient:        httpClient,
		paymentStore:      paymentStore,
		subscriptionStore: subscriptionStore,
	}
}

// InitiateSubscriptionPayment creates a payment session for a subscription plan
func (s *ArifpayService) InitiateSubscriptionPayment(ctx context.Context, userID int64, req domain.InitiateSubscriptionPaymentRequest) (*domain.InitiateSubscriptionPaymentResponse, error) {
	// Get the subscription plan
	plan, err := s.subscriptionStore.GetSubscriptionPlanByID(ctx, req.PlanID)
	if err != nil {
		return nil, fmt.Errorf("failed to get subscription plan: %w", err)
	}

	if !plan.IsActive {
		return nil, errors.New("subscription plan is not active")
	}

	// Check if user already has an active subscription
	hasActive, err := s.subscriptionStore.HasActiveSubscription(ctx, userID)
	if err != nil {
		return nil, fmt.Errorf("failed to check active subscription: %w", err)
	}
	if hasActive {
		return nil, errors.New("user already has an active subscription")
	}

	// Generate unique nonce
	nonce := uuid.NewString()
	expiresAt := time.Now().Add(3 * time.Hour)

	// Create payment record
	payment, err := s.paymentStore.CreatePayment(ctx, domain.CreatePaymentInput{
		UserID:    userID,
		PlanID:    &req.PlanID,
		Amount:    plan.Price,
		Currency:  plan.Currency,
		Nonce:     nonce,
		ExpiresAt: &expiresAt,
	})
	if err != nil {
		return nil, fmt.Errorf("failed to create payment record: %w", err)
	}

	// Create ArifPay checkout session
	checkoutReq := domain.CheckoutSessionRequest{
		CancelURL:      s.cfg.ARIFPAY.CancelUrl,
		Phone:          formatPhone(req.Phone),
		Email:          req.Email,
		Nonce:          nonce,
		SuccessURL:     s.cfg.ARIFPAY.SuccessUrl,
		ErrorURL:       s.cfg.ARIFPAY.ErrorUrl,
		NotifyURL:      s.cfg.ARIFPAY.C2BNotifyUrl,
		PaymentMethods: s.cfg.ARIFPAY.PaymentMethods,
		ExpireDate:     expiresAt.Format("2006-01-02T15:04:05"),
		Items: []struct {
			Name        string  `json:"name"`
			Quantity    int     `json:"quantity"`
			Price       float64 `json:"price"`
			Description string  `json:"description"`
		}{
			{
				Name:        plan.Name,
				Quantity:    1,
				Price:       plan.Price,
				Description: fmt.Sprintf("Subscription: %s", plan.Name),
			},
		},
		Beneficiaries: []struct {
			AccountNumber string  `json:"accountNumber"`
			Bank          string  `json:"bank"`
			Amount        float64 `json:"amount"`
		}{
			{
				AccountNumber: s.cfg.ARIFPAY.BeneficiaryAccountNumber,
				Bank:          s.cfg.ARIFPAY.Bank,
				Amount:        plan.Price,
			},
		},
		Lang: s.cfg.ARIFPAY.Lang,
	}

	// Marshal to JSON
	payload, err := json.Marshal(checkoutReq)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal checkout request: %w", err)
	}

	// Send request to Arifpay API
	url := fmt.Sprintf("%s/api/checkout/session", s.cfg.ARIFPAY.BaseURL)
	httpReq, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(payload))
	if err != nil {
		return nil, err
	}
	httpReq.Header.Set("Content-Type", "application/json")
	httpReq.Header.Set("x-arifpay-key", s.cfg.ARIFPAY.APIKey)

	resp, err := s.httpClient.Do(httpReq)
	if err != nil {
		return nil, fmt.Errorf("failed to call ArifPay API: %w", err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("ArifPay API error: %s", string(body))
	}

	var result map[string]interface{}
	if err := json.Unmarshal(body, &result); err != nil {
		return nil, fmt.Errorf("invalid response from ArifPay: %w", err)
	}

	data, ok := result["data"].(map[string]interface{})
	if !ok {
		return nil, errors.New("invalid response structure from ArifPay")
	}

	sessionID := fmt.Sprintf("%v", data["sessionId"])
	paymentURL := fmt.Sprintf("%v", data["paymentUrl"])

	// Update payment with session info
	if err := s.paymentStore.UpdatePaymentSessionID(ctx, payment.ID, sessionID, paymentURL); err != nil {
		return nil, fmt.Errorf("failed to update payment session: %w", err)
	}

	return &domain.InitiateSubscriptionPaymentResponse{
		PaymentID:  payment.ID,
		SessionID:  sessionID,
		PaymentURL: paymentURL,
		Amount:     plan.Price,
		Currency:   plan.Currency,
		ExpiresAt:  expiresAt.Format(time.RFC3339),
	}, nil
}

// ProcessPaymentWebhook handles the webhook callback from ArifPay
func (s *ArifpayService) ProcessPaymentWebhook(ctx context.Context, req domain.WebhookRequest) error {
	// Get payment by nonce
	payment, err := s.paymentStore.GetPaymentByNonce(ctx, req.Nonce)
	if err != nil {
		return fmt.Errorf("payment not found for nonce %s: %w", req.Nonce, err)
	}

	if payment.Status == string(domain.PaymentStatusSuccess) {
		return ErrPaymentAlreadyPaid
	}

	transactionStatus := strings.ToUpper(req.Transaction.TransactionStatus)
	if transactionStatus == "" {
		transactionStatus = strings.ToUpper(req.TransactionStatus)
	}

	var newStatus string
	switch transactionStatus {
	case "SUCCESS", "COMPLETED":
		newStatus = string(domain.PaymentStatusSuccess)
	case "FAILED", "REJECTED":
		newStatus = string(domain.PaymentStatusFailed)
	case "CANCELLED":
		newStatus = string(domain.PaymentStatusCancelled)
	case "PENDING", "PROCESSING":
		newStatus = string(domain.PaymentStatusProcessing)
	default:
		newStatus = string(domain.PaymentStatusPending)
	}

	// Update payment status
	if err := s.paymentStore.UpdatePaymentStatusByNonce(
		ctx,
		req.Nonce,
		newStatus,
		req.Transaction.TransactionID,
		req.PaymentMethod,
	); err != nil {
		return fmt.Errorf("failed to update payment status: %w", err)
	}

	// If payment succeeded, create the subscription
	if newStatus == string(domain.PaymentStatusSuccess) && payment.PlanID != nil {
		plan, err := s.subscriptionStore.GetSubscriptionPlanByID(ctx, *payment.PlanID)
		if err != nil {
			return fmt.Errorf("failed to get subscription plan: %w", err)
		}

		startsAt := time.Now()
		expiresAt := domain.CalculateExpiryDate(startsAt, plan.DurationValue, plan.DurationUnit)
		activeStatus := string(domain.SubscriptionStatusActive)
		autoRenew := false
		paymentRef := payment.Nonce
		paymentMethod := req.PaymentMethod

		subscription, err := s.subscriptionStore.CreateUserSubscription(ctx, domain.CreateUserSubscriptionInput{
			UserID:           payment.UserID,
			PlanID:           *payment.PlanID,
			StartsAt:         &startsAt,
			ExpiresAt:        expiresAt,
			Status:           &activeStatus,
			PaymentReference: &paymentRef,
			PaymentMethod:    &paymentMethod,
			AutoRenew:        &autoRenew,
		})
		if err != nil {
			return fmt.Errorf("failed to create subscription: %w", err)
		}

		// Link payment to subscription
		if err := s.paymentStore.LinkPaymentToSubscription(ctx, payment.ID, subscription.ID); err != nil {
			return fmt.Errorf("failed to link payment to subscription: %w", err)
		}
	}

	return nil
}

// VerifyPayment checks the status of a payment with ArifPay
func (s *ArifpayService) VerifyPayment(ctx context.Context, sessionID string) (*domain.Payment, error) {
	// Get local payment record
	payment, err := s.paymentStore.GetPaymentBySessionID(ctx, sessionID)
	if err != nil {
		return nil, ErrPaymentNotFound
	}

	// If already success or failed, return cached result
	if payment.Status == string(domain.PaymentStatusSuccess) ||
		payment.Status == string(domain.PaymentStatusFailed) {
		return payment, nil
	}

	// Call ArifPay to verify
	endpoint := fmt.Sprintf("%s/api/ms/transaction/status/%s", s.cfg.ARIFPAY.BaseURL, sessionID)
	httpReq, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
	if err != nil {
		return nil, err
	}
	httpReq.Header.Set("x-arifpay-key", s.cfg.ARIFPAY.APIKey)

	resp, err := s.httpClient.Do(httpReq)
	if err != nil {
		return nil, fmt.Errorf("failed to verify with ArifPay: %w", err)
	}
	defer resp.Body.Close()

	respBytes, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("ArifPay verify API error: %s", string(respBytes))
	}

	var result domain.WebhookRequest
	if err := json.Unmarshal(respBytes, &result); err != nil {
		return nil, fmt.Errorf("failed to parse ArifPay response: %w", err)
	}

	// Process the verification result same as webhook
	if err := s.ProcessPaymentWebhook(ctx, result); err != nil && err != ErrPaymentAlreadyPaid {
		return nil, err
	}

	// Return updated payment
	return s.paymentStore.GetPaymentBySessionID(ctx, sessionID)
}

// GetPaymentsByUser returns payment history for a user
func (s *ArifpayService) GetPaymentsByUser(ctx context.Context, userID int64, limit, offset int32) ([]domain.Payment, error) {
	return s.paymentStore.GetPaymentsByUserID(ctx, userID, limit, offset)
}

// GetPaymentByID returns a specific payment
func (s *ArifpayService) GetPaymentByID(ctx context.Context, id int64) (*domain.Payment, error) {
	return s.paymentStore.GetPaymentByID(ctx, id)
}

// CancelPayment cancels a pending payment
func (s *ArifpayService) CancelPayment(ctx context.Context, paymentID int64, userID int64) error {
	payment, err := s.paymentStore.GetPaymentByID(ctx, paymentID)
	if err != nil {
		return ErrPaymentNotFound
	}

	if payment.UserID != userID {
		return errors.New("unauthorized")
	}

	if payment.Status != string(domain.PaymentStatusPending) {
		return ErrInvalidPaymentState
	}

	return s.paymentStore.UpdatePaymentStatus(ctx, paymentID, string(domain.PaymentStatusCancelled))
}

// CreateCheckoutSession creates a generic checkout session (for non-subscription payments)
func (s *ArifpayService) CreateCheckoutSession(req domain.CheckoutSessionClientRequest, isDeposit bool, userId int64) (map[string]any, error) {
	nonce := uuid.NewString()

	var NotifyURL string
	if isDeposit {
		NotifyURL = s.cfg.ARIFPAY.C2BNotifyUrl
	} else {
		NotifyURL = s.cfg.ARIFPAY.B2CNotifyUrl
	}

	checkoutReq := domain.CheckoutSessionRequest{
		CancelURL:      s.cfg.ARIFPAY.CancelUrl,
		Phone:          formatPhone(req.CustomerPhone),
		Email:          req.CustomerEmail,
		Nonce:          nonce,
		SuccessURL:     s.cfg.ARIFPAY.SuccessUrl,
		ErrorURL:       s.cfg.ARIFPAY.ErrorUrl,
		NotifyURL:      NotifyURL,
		PaymentMethods: s.cfg.ARIFPAY.PaymentMethods,
		ExpireDate:     s.cfg.ARIFPAY.ExpireDate,
		Items: []struct {
			Name        string  `json:"name"`
			Quantity    int     `json:"quantity"`
			Price       float64 `json:"price"`
			Description string  `json:"description"`
		}{
			{
				Name:        s.cfg.ARIFPAY.ItemName,
				Quantity:    s.cfg.ARIFPAY.Quantity,
				Price:       req.Amount,
				Description: s.cfg.ARIFPAY.Description,
			},
		},
		Beneficiaries: []struct {
			AccountNumber string  `json:"accountNumber"`
			Bank          string  `json:"bank"`
			Amount        float64 `json:"amount"`
		}{
			{
				AccountNumber: s.cfg.ARIFPAY.BeneficiaryAccountNumber,
				Bank:          s.cfg.ARIFPAY.Bank,
				Amount:        req.Amount,
			},
		},
		Lang: s.cfg.ARIFPAY.Lang,
	}

	payload, err := json.Marshal(checkoutReq)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal checkout request: %w", err)
	}

	url := fmt.Sprintf("%s/api/checkout/session", s.cfg.ARIFPAY.BaseURL)
	httpReq, err := http.NewRequest("POST", url, bytes.NewBuffer(payload))
	if err != nil {
		return nil, err
	}
	httpReq.Header.Set("Content-Type", "application/json")
	httpReq.Header.Set("x-arifpay-key", s.cfg.ARIFPAY.APIKey)

	resp, err := s.httpClient.Do(httpReq)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("failed to create checkout session: %s", string(body))
	}

	var result map[string]interface{}
	if err := json.Unmarshal(body, &result); err != nil {
		return nil, fmt.Errorf("invalid response from Arifpay: %w", err)
	}

	data := result["data"].(map[string]interface{})
	return data, nil
}

// CancelCheckoutSession cancels an existing checkout session
func (s *ArifpayService) CancelCheckoutSession(ctx context.Context, sessionID string) (any, error) {
	url := fmt.Sprintf("%s/api/sandbox/checkout/session/%s", s.cfg.ARIFPAY.BaseURL, sessionID)

	req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("x-arifpay-key", s.cfg.ARIFPAY.APIKey)

	resp, err := s.httpClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("failed to execute cancel request: %w", err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("failed to read cancel response: %w", err)
	}

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("cancel request failed: status=%d, body=%s", resp.StatusCode, string(body))
	}

	var cancelResp domain.CancelCheckoutSessionResponse
	if err := json.Unmarshal(body, &cancelResp); err != nil {
		return nil, fmt.Errorf("failed to unmarshal cancel response: %w", err)
	}

	return cancelResp.Data, nil
}

// VerifyTransactionBySessionID verifies a transaction by session ID
func (s *ArifpayService) VerifyTransactionBySessionID(ctx context.Context, sessionID string) (*domain.WebhookRequest, error) {
	endpoint := fmt.Sprintf("%s/api/ms/transaction/status/%s", s.cfg.ARIFPAY.BaseURL, sessionID)

	httpReq, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}

	httpReq.Header.Set("x-arifpay-key", s.cfg.ARIFPAY.APIKey)

	resp, err := s.httpClient.Do(httpReq)
	if err != nil {
		return nil, fmt.Errorf("failed to call verify transaction API: %w", err)
	}
	defer resp.Body.Close()

	respBytes, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("failed to read response: %w", err)
	}

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("API returned status %d: %s", resp.StatusCode, string(respBytes))
	}

	var result domain.WebhookRequest
	if err := json.Unmarshal(respBytes, &result); err != nil {
		return nil, fmt.Errorf("failed to unmarshal response: %w", err)
	}

	return &result, nil
}

// VerifyTransactionByTransactionID verifies a transaction by transaction ID
func (s *ArifpayService) VerifyTransactionByTransactionID(ctx context.Context, req domain.ArifpayVerifyByTransactionIDRequest) (*domain.WebhookRequest, error) {
	endpoint := fmt.Sprintf("%s/api/checkout/getSessionByTransactionId", s.cfg.ARIFPAY.BaseURL)

	bodyBytes, err := json.Marshal(req)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal request: %w", err)
	}

	httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewBuffer(bodyBytes))
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}

	httpReq.Header.Set("x-arifpay-key", s.cfg.ARIFPAY.APIKey)

	resp, err := s.httpClient.Do(httpReq)
	if err != nil {
		return nil, fmt.Errorf("failed to call verify transaction API: %w", err)
	}
	defer resp.Body.Close()

	respBytes, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("failed to read response: %w", err)
	}

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("API returned status %d: %s", resp.StatusCode, string(respBytes))
	}

	var result domain.WebhookRequest
	if err := json.Unmarshal(respBytes, &result); err != nil {
		return nil, fmt.Errorf("failed to unmarshal response: %w", err)
	}

	return &result, nil
}

// GetPaymentMethodsMapping returns the list of payment methods
func (s *ArifpayService) GetPaymentMethodsMapping() []domain.ARIFPAYPaymentMethod {
	return []domain.ARIFPAYPaymentMethod{
		{ID: 1, Name: "ACCOUNT"},
		{ID: 2, Name: "NONYMOUS_ACCOUNT"},
		{ID: 3, Name: "ANONYMOUS_CARD"},
		{ID: 4, Name: "TELEBIRR"},
		{ID: 5, Name: "AWASH"},
		{ID: 6, Name: "AWASH_WALLET"},
		{ID: 7, Name: "PSS"},
		{ID: 8, Name: "CBE"},
		{ID: 9, Name: "AMOLE"},
		{ID: 10, Name: "BOA"},
		{ID: 11, Name: "KACHA"},
		{ID: 12, Name: "ETHSWITCH"},
		{ID: 13, Name: "TELEBIRR_USSD"},
		{ID: 14, Name: "HELLOCASH"},
		{ID: 15, Name: "MPESSA"},
	}
}

// =====================
// Direct Payment Methods (OTP-based)
// =====================

// InitiateDirectPayment creates a session and initiates direct payment (triggers OTP)
func (s *ArifpayService) InitiateDirectPayment(ctx context.Context, userID int64, req domain.InitiateDirectPaymentRequest) (*domain.InitiateDirectPaymentResponse, error) {
	// Get the subscription plan
	plan, err := s.subscriptionStore.GetSubscriptionPlanByID(ctx, req.PlanID)
	if err != nil {
		return nil, fmt.Errorf("failed to get subscription plan: %w", err)
	}

	if !plan.IsActive {
		return nil, errors.New("subscription plan is not active")
	}

	// Check if user already has an active subscription
	hasActive, err := s.subscriptionStore.HasActiveSubscription(ctx, userID)
	if err != nil {
		return nil, fmt.Errorf("failed to check active subscription: %w", err)
	}
	if hasActive {
		return nil, errors.New("user already has an active subscription")
	}

	// Generate unique nonce
	nonce := uuid.NewString()
	expiresAt := time.Now().Add(15 * time.Minute) // Shorter expiry for direct payments

	// Create payment record
	paymentMethod := string(req.PaymentMethod)
	payment, err := s.paymentStore.CreatePayment(ctx, domain.CreatePaymentInput{
		UserID:        userID,
		PlanID:        &req.PlanID,
		Amount:        plan.Price,
		Currency:      plan.Currency,
		Nonce:         nonce,
		PaymentMethod: &paymentMethod,
		ExpiresAt:     &expiresAt,
	})
	if err != nil {
		return nil, fmt.Errorf("failed to create payment record: %w", err)
	}

	// Create ArifPay checkout session with specific payment method
	checkoutReq := domain.CheckoutSessionRequest{
		CancelURL:      s.cfg.ARIFPAY.CancelUrl,
		Phone:          formatPhone(req.Phone),
		Email:          req.Email,
		Nonce:          nonce,
		SuccessURL:     s.cfg.ARIFPAY.SuccessUrl,
		ErrorURL:       s.cfg.ARIFPAY.ErrorUrl,
		NotifyURL:      s.cfg.ARIFPAY.C2BNotifyUrl,
		PaymentMethods: []string{string(req.PaymentMethod)},
		ExpireDate:     expiresAt.Format("2006-01-02T15:04:05"),
		Items: []struct {
			Name        string  `json:"name"`
			Quantity    int     `json:"quantity"`
			Price       float64 `json:"price"`
			Description string  `json:"description"`
		}{
			{
				Name:        plan.Name,
				Quantity:    1,
				Price:       plan.Price,
				Description: fmt.Sprintf("Subscription: %s", plan.Name),
			},
		},
		Beneficiaries: []struct {
			AccountNumber string  `json:"accountNumber"`
			Bank          string  `json:"bank"`
			Amount        float64 `json:"amount"`
		}{
			{
				AccountNumber: s.cfg.ARIFPAY.BeneficiaryAccountNumber,
				Bank:          s.cfg.ARIFPAY.Bank,
				Amount:        plan.Price,
			},
		},
		Lang: s.cfg.ARIFPAY.Lang,
	}

	// Create session
	payload, err := json.Marshal(checkoutReq)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal checkout request: %w", err)
	}

	url := fmt.Sprintf("%s/api/checkout/session", s.cfg.ARIFPAY.BaseURL)
	httpReq, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(payload))
	if err != nil {
		return nil, err
	}
	httpReq.Header.Set("Content-Type", "application/json")
	httpReq.Header.Set("x-arifpay-key", s.cfg.ARIFPAY.APIKey)

	resp, err := s.httpClient.Do(httpReq)
	if err != nil {
		return nil, fmt.Errorf("failed to call ArifPay API: %w", err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("ArifPay API error: %s", string(body))
	}

	var result map[string]interface{}
	if err := json.Unmarshal(body, &result); err != nil {
		return nil, fmt.Errorf("invalid response from ArifPay: %w", err)
	}

	data, ok := result["data"].(map[string]interface{})
	if !ok {
		return nil, errors.New("invalid response structure from ArifPay")
	}

	sessionID := fmt.Sprintf("%v", data["sessionId"])

	// Update payment with session info
	if err := s.paymentStore.UpdatePaymentSessionID(ctx, payment.ID, sessionID, ""); err != nil {
		return nil, fmt.Errorf("failed to update payment session: %w", err)
	}

	// Now initiate direct transfer based on payment method
	directResp, err := s.initiateDirectTransfer(ctx, sessionID, formatPhone(req.Phone), req.PaymentMethod)
	if err != nil {
		// Update payment status to failed
		s.paymentStore.UpdatePaymentStatus(ctx, payment.ID, string(domain.PaymentStatusFailed))
		return nil, fmt.Errorf("failed to initiate direct transfer: %w", err)
	}

	requiresOTP := s.paymentMethodRequiresOTP(req.PaymentMethod)
	message := "Payment initiated"
	if requiresOTP {
		message = "OTP sent to your phone. Please verify to complete payment."
	} else {
		message = directResp
	}

	return &domain.InitiateDirectPaymentResponse{
		PaymentID:   payment.ID,
		SessionID:   sessionID,
		RequiresOTP: requiresOTP,
		Message:     message,
		Amount:      plan.Price,
		Currency:    plan.Currency,
	}, nil
}

// initiateDirectTransfer calls the appropriate direct transfer endpoint
func (s *ArifpayService) initiateDirectTransfer(ctx context.Context, sessionID, phone string, method domain.DirectPaymentMethod) (string, error) {
	var endpoint string

	switch method {
	case domain.DirectPaymentTelebirr, domain.DirectPaymentTelebirrUSSD:
		endpoint = fmt.Sprintf("%s/api/checkout/telebirr/direct/transfer", s.cfg.ARIFPAY.BaseURL)
	case domain.DirectPaymentCBE:
		endpoint = fmt.Sprintf("%s/api/checkout/cbe/direct/transfer", s.cfg.ARIFPAY.BaseURL)
	case domain.DirectPaymentAmole:
		endpoint = fmt.Sprintf("%s/api/checkout/amole/direct/transfer", s.cfg.ARIFPAY.BaseURL)
	case domain.DirectPaymentHelloCash:
		endpoint = fmt.Sprintf("%s/api/checkout/hellocash/direct/transfer", s.cfg.ARIFPAY.BaseURL)
	case domain.DirectPaymentAwash:
		endpoint = fmt.Sprintf("%s/api/checkout/awash/direct/transfer", s.cfg.ARIFPAY.BaseURL)
	case domain.DirectPaymentMPesa:
		endpoint = fmt.Sprintf("%s/api/Mpesa/c2b/transfer", s.cfg.ARIFPAY.BaseURL)
	default:
		return "", fmt.Errorf("unsupported direct payment method: %s", method)
	}

	reqBody := map[string]string{
		"sessionId":   sessionID,
		"phoneNumber": phone,
	}

	payload, err := json.Marshal(reqBody)
	if err != nil {
		return "", err
	}

	httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewBuffer(payload))
	if err != nil {
		return "", err
	}
	httpReq.Header.Set("Content-Type", "application/json")
	httpReq.Header.Set("x-arifpay-key", s.cfg.ARIFPAY.APIKey)

	resp, err := s.httpClient.Do(httpReq)
	if err != nil {
		return "", fmt.Errorf("failed to initiate direct transfer: %w", err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}

	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("direct transfer failed: %s", string(body))
	}

	var result map[string]interface{}
	if err := json.Unmarshal(body, &result); err != nil {
		return string(body), nil
	}

	if msg, ok := result["msg"].(string); ok {
		return msg, nil
	}
	if msg, ok := result["message"].(string); ok {
		return msg, nil
	}

	return "Transfer initiated successfully", nil
}

// VerifyDirectPaymentOTP verifies the OTP for direct payment methods
func (s *ArifpayService) VerifyDirectPaymentOTP(ctx context.Context, userID int64, req domain.VerifyOTPRequest) (*domain.VerifyOTPResponse, error) {
	// Get payment by session ID
	payment, err := s.paymentStore.GetPaymentBySessionID(ctx, req.SessionID)
	if err != nil {
		return nil, ErrPaymentNotFound
	}

	// Verify ownership
	if payment.UserID != userID {
		return nil, errors.New("unauthorized")
	}

	// Check payment status
	if payment.Status == string(domain.PaymentStatusSuccess) {
		return &domain.VerifyOTPResponse{
			Success:   true,
			Message:   "Payment already completed",
			PaymentID: payment.ID,
		}, nil
	}

	if payment.Status != string(domain.PaymentStatusPending) && payment.Status != string(domain.PaymentStatusProcessing) {
		return nil, ErrInvalidPaymentState
	}

	// Determine OTP verification endpoint based on payment method
	paymentMethod := ""
	if payment.PaymentMethod != nil {
		paymentMethod = *payment.PaymentMethod
	}

	endpoint, err := s.getOTPVerifyEndpoint(domain.DirectPaymentMethod(paymentMethod))
	if err != nil {
		return nil, err
	}

	// Call OTP verification
	reqBody := map[string]string{
		"sessionId": req.SessionID,
		"otp":       req.OTP,
	}
	// Some endpoints use different field names
	if paymentMethod == string(domain.DirectPaymentAmole) {
		reqBody["SessionId"] = req.SessionID
	}

	payload, err := json.Marshal(reqBody)
	if err != nil {
		return nil, err
	}

	httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewBuffer(payload))
	if err != nil {
		return nil, err
	}
	httpReq.Header.Set("Content-Type", "application/json")
	httpReq.Header.Set("x-arifpay-key", s.cfg.ARIFPAY.APIKey)

	resp, err := s.httpClient.Do(httpReq)
	if err != nil {
		return nil, fmt.Errorf("failed to verify OTP: %w", err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	var result map[string]interface{}
	json.Unmarshal(body, &result)

	if resp.StatusCode != http.StatusOK {
		errMsg := "OTP verification failed"
		if msg, ok := result["msg"].(string); ok {
			errMsg = msg
		} else if msg, ok := result["message"].(string); ok {
			errMsg = msg
		}
		return &domain.VerifyOTPResponse{
			Success: false,
			Message: errMsg,
		}, nil
	}

	// OTP verified successfully - update payment status
	transactionID := ""
	if tid, ok := result["transactionId"].(string); ok {
		transactionID = tid
	}

	// Update payment to success
	if err := s.paymentStore.UpdatePaymentStatusBySessionID(
		ctx,
		req.SessionID,
		string(domain.PaymentStatusSuccess),
		transactionID,
		paymentMethod,
	); err != nil {
		return nil, fmt.Errorf("failed to update payment status: %w", err)
	}

	// Create subscription
	if payment.PlanID != nil {
		plan, err := s.subscriptionStore.GetSubscriptionPlanByID(ctx, *payment.PlanID)
		if err == nil {
			startsAt := time.Now()
			expiresAt := domain.CalculateExpiryDate(startsAt, plan.DurationValue, plan.DurationUnit)
			activeStatus := string(domain.SubscriptionStatusActive)
			autoRenew := false
			paymentRef := payment.Nonce

			subscription, err := s.subscriptionStore.CreateUserSubscription(ctx, domain.CreateUserSubscriptionInput{
				UserID:           payment.UserID,
				PlanID:           *payment.PlanID,
				StartsAt:         &startsAt,
				ExpiresAt:        expiresAt,
				Status:           &activeStatus,
				PaymentReference: &paymentRef,
				PaymentMethod:    &paymentMethod,
				AutoRenew:        &autoRenew,
			})
			if err == nil {
				s.paymentStore.LinkPaymentToSubscription(ctx, payment.ID, subscription.ID)
			}
		}
	}

	return &domain.VerifyOTPResponse{
		Success:       true,
		Message:       "Payment completed successfully",
		TransactionID: transactionID,
		PaymentID:     payment.ID,
	}, nil
}

// getOTPVerifyEndpoint returns the OTP verification endpoint for the payment method
func (s *ArifpayService) getOTPVerifyEndpoint(method domain.DirectPaymentMethod) (string, error) {
	switch method {
	case domain.DirectPaymentAmole:
		return fmt.Sprintf("%s/api/checkout/amole/direct/verifyOTP", s.cfg.ARIFPAY.BaseURL), nil
	case domain.DirectPaymentHelloCash:
		return fmt.Sprintf("%s/api/checkout/hellocash/direct/verify", s.cfg.ARIFPAY.BaseURL), nil
	case domain.DirectPaymentCBE:
		return fmt.Sprintf("%s/api/checkout/cbe/direct/verify", s.cfg.ARIFPAY.BaseURL), nil
	case domain.DirectPaymentAwash:
		return fmt.Sprintf("%s/api/checkout/awash/direct/verify", s.cfg.ARIFPAY.BaseURL), nil
	default:
		return "", fmt.Errorf("payment method %s does not require OTP verification", method)
	}
}

// paymentMethodRequiresOTP checks if the payment method requires OTP verification
func (s *ArifpayService) paymentMethodRequiresOTP(method domain.DirectPaymentMethod) bool {
	switch method {
	case domain.DirectPaymentAmole, domain.DirectPaymentHelloCash, domain.DirectPaymentAwash:
		return true
	case domain.DirectPaymentTelebirr, domain.DirectPaymentTelebirrUSSD, domain.DirectPaymentCBE, domain.DirectPaymentMPesa:
		// These use push notification or USSD, no OTP needed in API
		return false
	default:
		return false
	}
}

// GetDirectPaymentMethods returns payment methods that support direct payment
func (s *ArifpayService) GetDirectPaymentMethods() []domain.ARIFPAYPaymentMethod {
	return []domain.ARIFPAYPaymentMethod{
		{ID: 1, Name: string(domain.DirectPaymentTelebirr)},
		{ID: 2, Name: string(domain.DirectPaymentTelebirrUSSD)},
		{ID: 3, Name: string(domain.DirectPaymentCBE)},
		{ID: 4, Name: string(domain.DirectPaymentAmole)},
		{ID: 5, Name: string(domain.DirectPaymentHelloCash)},
		{ID: 6, Name: string(domain.DirectPaymentAwash)},
		{ID: 7, Name: string(domain.DirectPaymentMPesa)},
	}
}

// Helper to format phone number
func formatPhone(phone string) string {
	phone = strings.TrimSpace(phone)
	if strings.HasPrefix(phone, "+251") {
		return strings.TrimPrefix(phone, "+")
	}
	if strings.HasPrefix(phone, "0") {
		return "251" + phone[1:]
	}
	if strings.HasPrefix(phone, "251") {
		return phone
	}
	return "251" + phone
}
