package wallet

import (
	"context"
	"errors"
	"fmt"

	"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
)

var (
	ErrWalletNotTransferable = errors.New("wallet is not transferable")
	ErrInsufficientBalance   = errors.New("wallet balance is insufficient")
)

func (s *Service) CreateTransfer(ctx context.Context, transfer domain.CreateTransfer) (domain.Transfer, error) {
	senderWallet, err := s.walletStore.GetWalletByID(ctx, transfer.SenderWalletID)
	receiverWallet, err := s.walletStore.GetWalletByID(ctx, transfer.ReceiverWalletID)
	if err != nil {
		return domain.Transfer{}, fmt.Errorf("failed to get sender wallet: %w", err)
	}

	// Check if wallet has sufficient balance
	if senderWallet.Balance < transfer.Amount || senderWallet.Balance == 0 {
		// Send notification to customer
		customerNotification := &domain.Notification{
			RecipientID:     receiverWallet.UserID,
			Type:            domain.NOTIFICATION_TYPE_TRANSFER,
			Level:           domain.NotificationLevelError,
			Reciever:        domain.NotificationRecieverSideCustomer,
			DeliveryChannel: domain.DeliveryChannelInApp,
			Payload: domain.NotificationPayload{
				Headline: "Service Temporarily Unavailable",
				Message:  "Our payment system is currently under maintenance. Please try again later.",
			},
			Priority: 2,
			Metadata: []byte(fmt.Sprintf(`{
                "transfer_amount": %d,
                "current_balance": %d,
                "wallet_id": %d,
                "notification_type": "customer_facing"
            }`, transfer.Amount, senderWallet.Balance, transfer.SenderWalletID)),
		}

		// Send notification to admin team
		adminNotification := &domain.Notification{
			RecipientID:     senderWallet.UserID,
			Type:            domain.NOTIFICATION_TYPE_ADMIN_ALERT,
			Level:           domain.NotificationLevelError,
			Reciever:        domain.NotificationRecieverSideAdmin,
			DeliveryChannel: domain.DeliveryChannelEmail, // Or any preferred admin channel
			Payload: domain.NotificationPayload{
				Headline: "CREDIT WARNING: System Running Out of Funds",
				Message: fmt.Sprintf(
					"Wallet ID %d has insufficient balance for transfer. Current balance: %.2f, Attempted transfer: %.2f",
					transfer.SenderWalletID,
					float64(senderWallet.Balance)/100,
					float64(transfer.Amount)/100,
				),
			},
			Priority: 1, // High priority for admin alerts
			Metadata: fmt.Appendf(nil, `{
				"wallet_id": %d,
				"balance": %d,
				"required_amount": %d,
				"notification_type": "admin_alert"
			}`, transfer.SenderWalletID, senderWallet.Balance, transfer.Amount),
		}

		// Send both notifications
		if err := s.notificationStore.SendNotification(ctx, customerNotification); err != nil {
			s.logger.Error("failed to send customer notification",
				"user_id", "",
				"error", err)
		}

		// Get admin recipients and send to all
		adminRecipients, err := s.notificationStore.ListRecipientIDs(ctx, domain.NotificationRecieverSideAdmin)
		if err != nil {
			s.logger.Error("failed to get admin recipients", "error", err)
		} else {
			for _, adminID := range adminRecipients {
				adminNotification.RecipientID = adminID
				if err := s.notificationStore.SendNotification(ctx, adminNotification); err != nil {
					s.logger.Error("failed to send admin notification",
						"admin_id", adminID,
						"error", err)
				}
			}
		}

		return domain.Transfer{}, ErrInsufficientBalance
	}

	// Proceed with transfer if balance is sufficient
	return s.transferStore.CreateTransfer(ctx, transfer)
}

func (s *Service) GetAllTransfers(ctx context.Context) ([]domain.Transfer, error) {
	return s.transferStore.GetAllTransfers(ctx)
}

func (s *Service) GetTransferByReference(ctx context.Context, reference string) (domain.Transfer, error) {
	return s.transferStore.GetTransferByReference(ctx, reference)
}

func (s *Service) GetTransferByID(ctx context.Context, id int64) (domain.Transfer, error) {
	return s.transferStore.GetTransferByID(ctx, id)
}

func (s *Service) GetTransfersByWallet(ctx context.Context, walletID int64) ([]domain.Transfer, error) {
	return s.transferStore.GetTransfersByWallet(ctx, walletID)
}

func (s *Service) UpdateTransferVerification(ctx context.Context, id int64, verified bool) error {
	return s.transferStore.UpdateTransferVerification(ctx, id, verified)
}

func (s *Service) UpdateTransferStatus(ctx context.Context, id int64, status string) error {
	return s.transferStore.UpdateTransferStatus(ctx, id, status)
}

func (s *Service) RefillWallet(ctx context.Context, transfer domain.CreateTransfer) (domain.Transfer, error) {
	receiverWallet, err := s.GetWalletByID(ctx, transfer.ReceiverWalletID)
	if err != nil {
		return domain.Transfer{}, err
	}

	// Add to receiver
	senderWallet, err := s.GetWalletByID(ctx, transfer.SenderWalletID)
	if err != nil {
		return domain.Transfer{}, err
	} else if senderWallet.Balance < transfer.Amount {
		return domain.Transfer{}, ErrInsufficientBalance
	}

	err = s.walletStore.UpdateBalance(ctx, receiverWallet.ID, receiverWallet.Balance+transfer.Amount)
	if err != nil {
		return domain.Transfer{}, err
	}

	// Log the transfer so that if there is a mistake, it can be reverted
	newTransfer, err := s.transferStore.CreateTransfer(ctx, domain.CreateTransfer{
		CashierID:        transfer.CashierID,
		ReceiverWalletID: receiverWallet.ID,
		Amount:           transfer.Amount,
		Type:             domain.DEPOSIT,
		PaymentMethod:    transfer.PaymentMethod,
		Verified:         true,
	})
	if err != nil {
		return domain.Transfer{}, err
	}

	return newTransfer, nil

}

func (s *Service) TransferToWallet(ctx context.Context, senderID int64, receiverID int64, amount domain.Currency, paymentMethod domain.PaymentMethod, cashierID domain.ValidInt64) (domain.Transfer, error) {

	senderWallet, err := s.GetWalletByID(ctx, senderID)
	if err != nil {
		return domain.Transfer{}, err
	}

	if senderWallet.IsTransferable {
		return domain.Transfer{}, ErrWalletNotTransferable
	}

	receiverWallet, err := s.GetWalletByID(ctx, receiverID)
	if err != nil {
		return domain.Transfer{}, err
	}

	if receiverWallet.IsTransferable {
		return domain.Transfer{}, ErrWalletNotTransferable
	}

	// Deduct from sender
	if senderWallet.Balance < amount {
		return domain.Transfer{}, ErrBalanceInsufficient
	}

	err = s.walletStore.UpdateBalance(ctx, senderID, senderWallet.Balance-amount)

	if err != nil {
		return domain.Transfer{}, err
	}

	// Add to receiver
	err = s.walletStore.UpdateBalance(ctx, receiverID, receiverWallet.Balance+amount)

	if err != nil {
		return domain.Transfer{}, err
	}

	// Log the transfer so that if there is a mistake, it can be reverted
	transfer, err := s.transferStore.CreateTransfer(ctx, domain.CreateTransfer{
		SenderWalletID:   senderID,
		CashierID:        cashierID,
		ReceiverWalletID: receiverID,
		Amount:           amount,
		Type:             domain.WALLET,
		PaymentMethod:    paymentMethod,
		Verified:         true,
	})
	if err != nil {
		return domain.Transfer{}, err
	}

	return transfer, nil
}
