package chapa

import (
	"context"
	"errors"
	"fmt"
	"strconv"

	"github.com/SamuelTariku/FortuneBet-Backend/internal/config"
	"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
	"github.com/SamuelTariku/FortuneBet-Backend/internal/services/user"
	"github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet"
	"github.com/google/uuid"
)

var (
	ErrPaymentNotFound      = errors.New("payment not found")
	ErrPaymentAlreadyExists = errors.New("payment with this reference already exists")
	ErrInvalidPaymentAmount = errors.New("invalid payment amount")
)

type Service struct {
	transferStore wallet.TransferStore
	walletStore   wallet.Service
	userStore     user.UserStore
	cfg           *config.Config
	chapaClient   *Client
}

func NewService(
	transferStore wallet.TransferStore,
	walletStore wallet.Service,
	userStore user.UserStore,
	chapaClient *Client,

) *Service {
	return &Service{
		transferStore: transferStore,
		walletStore:   walletStore,
		userStore:     userStore,
		chapaClient:   chapaClient,
	}
}

// InitiateDeposit starts a new deposit process
func (s *Service) InitiateDeposit(ctx context.Context, userID int64, amount domain.Currency) (string, error) {
	// Validate amount
	if amount <= 0 {
		return "", ErrInvalidPaymentAmount
	}

	// Get user details
	user, err := s.userStore.GetUserByID(ctx, userID)
	if err != nil {
		return "", fmt.Errorf("failed to get user: %w", err)
	}

	var senderWallet domain.Wallet

	// Generate unique reference
	reference := uuid.New().String()
	senderWallets, err := s.walletStore.GetWalletsByUser(ctx, userID)
	if err != nil {
		return "", fmt.Errorf("failed to get sender wallets: %w", err)
	}
	for _, wallet := range senderWallets {
		if wallet.IsWithdraw {
			senderWallet = wallet
			break
		}
	}

	// Check if payment with this reference already exists
	// if transfer, err := s.transferStore.GetTransferByReference(ctx, reference); err == nil {

	// 	return fmt.Sprintf("%v", transfer), ErrPaymentAlreadyExists
	// }

	// Create payment record
	transfer := domain.CreateTransfer{
		Amount:          amount,
		Type:            domain.DEPOSIT,
		PaymentMethod:   domain.TRANSFER_CHAPA,
		ReferenceNumber: reference,
		// ReceiverWalletID: 1,
		SenderWalletID: senderWallet.ID,
		Verified:       false,
	}

	if _, err := s.transferStore.CreateTransfer(ctx, transfer); err != nil {
		return "", fmt.Errorf("failed to save payment: %w", err)
	}

	// Initialize payment with Chapa
	response, err := s.chapaClient.InitializePayment(ctx, domain.ChapaDepositRequest{
		Amount:      amount,
		Currency:    "ETB",
		Email:       user.Email,
		FirstName:   user.FirstName,
		LastName:    user.LastName,
		TxRef:       reference,
		CallbackURL: "https://fortunebet.com/api/v1/payments/callback",
		ReturnURL:   "https://fortunebet.com/api/v1/payment-success",
	})

	if err != nil {
		// Update payment status to failed
		// _ = s.transferStore.(payment.ID, domain.PaymentStatusFailed)
		return "", fmt.Errorf("failed to initialize payment: %w", err)
	}

	return response.CheckoutURL, nil
}
func (s *Service) InitiateWithdrawal(ctx context.Context, userID int64, req domain.ChapaWithdrawalRequest) (*domain.Transfer, error) {
	// Parse and validate amount
	amount, err := strconv.ParseInt(req.Amount, 10, 64)
	if err != nil || amount <= 0 {
		return nil, domain.ErrInvalidWithdrawalAmount
	}

	// Get user details
	// user, err := s.userStore.GetUserByID(ctx, userID)
	// if err != nil {
	// 	return nil, fmt.Errorf("failed to get user: %w", err)
	// }

	// Get user's wallet
	wallets, err := s.walletStore.GetWalletsByUser(ctx, userID)
	if err != nil {
		return nil, fmt.Errorf("failed to get user wallets: %w", err)
	}

	var withdrawWallet domain.Wallet
	for _, wallet := range wallets {
		if wallet.IsWithdraw {
			withdrawWallet = wallet
			break
		}
	}

	if withdrawWallet.ID == 0 {
		return nil, errors.New("withdrawal wallet not found")
	}
	// Check balance
	if withdrawWallet.Balance < domain.Currency(amount) {
		return nil, domain.ErrInsufficientBalance
	}

	// Generate unique reference
	reference := uuid.New().String()

	createTransfer := domain.CreateTransfer{
		Amount:           domain.Currency(amount),
		Type:             domain.WITHDRAW,
		ReceiverWalletID: 1,
		SenderWalletID:   withdrawWallet.ID,
		Status:           string(domain.PaymentStatusPending),
		Verified:         false,
		ReferenceNumber:  reference,
		PaymentMethod:    domain.TRANSFER_CHAPA,
	}

	transfer, err := s.transferStore.CreateTransfer(ctx, createTransfer)

	if err != nil {
		return nil, fmt.Errorf("failed to create transfer record: %w", err)
	}
	// Initiate transfer with Chapa
	transferReq := domain.ChapaWithdrawalRequest{
		AccountName:   req.AccountName,
		AccountNumber: req.AccountNumber,
		Amount:        fmt.Sprintf("%d", amount),
		Currency:      req.Currency,
		Reference:     reference,
		// BeneficiaryName: fmt.Sprintf("%s %s", user.FirstName, user.LastName),
		BankCode: req.BankCode,
	}

	success, err := s.chapaClient.InitiateTransfer(ctx, transferReq)
	if err != nil || !success {
		// Update withdrawal status to failed
		_ = s.transferStore.UpdateTransferStatus(ctx, transfer.ID, string(domain.WithdrawalStatusFailed))
		return nil, fmt.Errorf("failed to initiate transfer: %w", err)
	}

	// Update withdrawal status to processing
	if err := s.transferStore.UpdateTransferStatus(ctx, transfer.ID, string(domain.WithdrawalStatusProcessing)); err != nil {
		return nil, fmt.Errorf("failed to update withdrawal status: %w", err)
	}
	// Deduct from wallet (or wait for webhook confirmation depending on your flow)
	newBalance := withdrawWallet.Balance - domain.Currency(amount)
	if err := s.walletStore.UpdateBalance(ctx, withdrawWallet.ID, newBalance); err != nil {
		return nil, fmt.Errorf("failed to update wallet balance: %w", err)
	}

	return &transfer, nil
}

func (s *Service) GetSupportedBanks(ctx context.Context) ([]domain.Bank, error) {
	banks, err := s.chapaClient.FetchSupportedBanks(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to fetch banks: %w", err)
	}
	return banks, nil
}

func (s *Service) ManualVerifTransaction(ctx context.Context, txRef string) (*domain.ChapaVerificationResponse, error) {
	// First check if we already have a verified record
	transfer, err := s.transferStore.GetTransferByReference(ctx, txRef)
	if err == nil && transfer.Verified {
		return &domain.ChapaVerificationResponse{
			Status:   string(domain.PaymentStatusCompleted),
			Amount:   float64(transfer.Amount) / 100, // Convert from cents/kobo
			Currency: "ETB",
		}, nil
	}

	// If not verified or not found, verify with Chapa
	verification, err := s.chapaClient.VerifyPayment(ctx, txRef)
	if err != nil {
		return nil, fmt.Errorf("failed to verify payment: %w", err)
	}

	// Update our records if payment is successful
	if verification.Status == domain.PaymentStatusCompleted {
		err = s.transferStore.UpdateTransferVerification(ctx, transfer.ID, true)
		if err != nil {
			return nil, fmt.Errorf("failed to update verification status: %w", err)
		}

		// Credit user's wallet
		err = s.walletStore.UpdateBalance(ctx, transfer.SenderWalletID, transfer.Amount)
		if err != nil {
			return nil, fmt.Errorf("failed to update wallet balance: %w", err)
		}
	}

	return &domain.ChapaVerificationResponse{
		Status:   string(verification.Status),
		Amount:   float64(verification.Amount),
		Currency: verification.Currency,
	}, nil
}

func (s *Service) HandleVerifyDepositWebhook(ctx context.Context, transfer domain.ChapaWebHookTransfer) error {
	// Find payment by reference
	payment, err := s.transferStore.GetTransferByReference(ctx, transfer.Reference)
	if err != nil {
		return ErrPaymentNotFound
	}

	if payment.Verified {
		return nil
	}

	// Verify payment with Chapa
	// verification, err := s.chapaClient.VerifyPayment(ctx, transfer.Reference)
	// if err != nil {
	// 	return fmt.Errorf("failed to verify payment: %w", err)
	// }

	// Update payment status
	// verified := false
	// if transfer.Status == string(domain.PaymentStatusCompleted) {
	// 	verified = true
	// }

	if err := s.transferStore.UpdateTransferVerification(ctx, payment.ID, true); err != nil {
		return fmt.Errorf("failed to update payment status: %w", err)
	}

	// If payment is completed, credit user's wallet
	if transfer.Status == string(domain.PaymentStatusCompleted) {
		if err := s.walletStore.AddToWallet(ctx, payment.SenderWalletID, payment.Amount); err != nil {
			return fmt.Errorf("failed to credit user wallet: %w", err)
		}
	}

	return nil
}

func (s *Service) HandleVerifyWithdrawWebhook(ctx context.Context, payment domain.ChapaWebHookPayment) error {
	// Find payment by reference
	transfer, err := s.transferStore.GetTransferByReference(ctx, payment.Reference)
	if err != nil {
		return ErrPaymentNotFound
	}

	if transfer.Verified {
		return nil
	}

	// Verify payment with Chapa
	// verification, err := s.chapaClient.VerifyPayment(ctx, payment.Reference)
	// if err != nil {
	// 	return fmt.Errorf("failed to verify payment: %w", err)
	// }

	// Update payment status
	// verified := false
	// if transfer.Status == string(domain.PaymentStatusCompleted) {
	// 	verified = true
	// }

	if err := s.transferStore.UpdateTransferVerification(ctx, transfer.ID, true); err != nil {
		return fmt.Errorf("failed to update payment status: %w", err)
	}

	// If payment is completed, credit user's wallet
	if payment.Status == string(domain.PaymentStatusFailed) {
		if err := s.walletStore.AddToWallet(ctx, transfer.SenderWalletID, transfer.Amount); err != nil {
			return fmt.Errorf("failed to credit user wallet: %w", err)
		}
	}

	return nil
}
