package service

import (
	"context"
	"errors"
	"fmt"
	"time"
)

type PaymentService struct {
	repo      PaymentRepository
	gwFactory GatewayFactory
}

type PaymentRepository interface {
	CreatePayment(ctx context.Context, p *Payment) error
	CreateTransaction(ctx context.Context, t *PaymentTransaction) error
	UpdateTransaction(ctx context.Context, t *PaymentTransaction) error
	GetGatewayByCode(ctx context.Context, code string) (*Gateway, error)
}

type GatewayFactory interface {
	GetGatewayAdapter(code string) (GatewayPort, error)
}

type GatewayPort interface {
	Request(amount int64, callbackURL string, description string) (token string, redirectURL string, rawReq []byte, rawRes []byte, err error)
}

func NewPaymentService(r PaymentRepository, gwf GatewayFactory) *PaymentService {
	return &PaymentService{
		repo:      r,
		gwFactory: gwf,
	}
}

func (s *PaymentService) InitiatePayment(ctx context.Context, req InitiatePaymentRequest) (*InitiatePaymentResponse, error) {

	gateway, err := s.repo.GetGatewayByCode(ctx, req.GatewayCode)
	if err != nil || !gateway.IsActive {
		return nil, errors.New("gateway is not available")
	}

	payment := &Payment{
		UserID:      req.UserID,
		PayableType: req.PayableType,
		PayableID:   req.PayableID,
		GatewayID:   gateway.ID,
		TotalAmount: req.Amount,
		Currency:    CurrencyIRR,
		Status:      PaymentStatusPending,
		CreatedAt:   time.Now(),
		UpdatedAt:   time.Now(),
	}
	if err := s.repo.CreatePayment(ctx, payment); err != nil {
		return nil, fmt.Errorf("failed to create payment: %w", err)
	}

	transaction := &PaymentTransaction{
		PaymentID: payment.ID,
		Type:      TransactionTypeRequest,
		Status:    TransactionStatusPending,
		CreatedAt: time.Now(),
		UpdatedAt: time.Now(),
	}
	if err := s.repo.CreateTransaction(ctx, transaction); err != nil {
		return nil, fmt.Errorf("failed to create initial transaction: %w", err)
	}

	gwAdapter, err := s.gwFactory.GetGatewayAdapter(gateway.Code)
	if err != nil {
		return nil, fmt.Errorf("failed to load gateway adapter: %w", err)
	}

	desc := fmt.Sprintf(" پرداخت برای %s شماره %d", req.PayableType, req.PayableID)
	token, redirectURL, rawReq, rawRes, gwErr := gwAdapter.Request(req.Amount, req.CallbackURL, desc)

	transaction.RequestData = rawReq
	transaction.ResponseData = rawRes
	transaction.UpdatedAt = time.Now()

	if gwErr != nil {
		transaction.Status = TransactionStatusFailed
		transaction.ErrorMessage = gwErr.Error()
		_ = s.repo.UpdateTransaction(ctx, transaction)
		return nil, fmt.Errorf("gateway request failed: %w", gwErr)
	}

	transaction.Status = TransactionStatusSuccess
	transaction.GatewayToken = token
	if err := s.repo.UpdateTransaction(ctx, transaction); err != nil {
		return nil, fmt.Errorf("failed to update transaction with token: %w", err)
	}

	return &InitiatePaymentResponse{
		PaymentID:   payment.ID,
		RedirectURL: redirectURL,
	}, nil
}
