package service

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

	"git.gocasts.ir/ebhomengo/niki/domain/payment/entity"
)

type PaymentService struct {
	paymentRepo       PaymentRepo
	paymentMethodRepo PaymentMethodRepo
	gwFactory         GatewayFactory
}

type PaymentRepo interface {
	CreatePayment(ctx context.Context, p *entity.Payment) error
	CreateTransaction(ctx context.Context, t *entity.PaymentTransaction) error
	UpdateTransaction(ctx context.Context, t *entity.PaymentTransaction) error
}
type PaymentMethodRepo interface {
	GetGatewayByCode(ctx context.Context, code string) (entity.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(pr PaymentRepo, pmr PaymentMethodRepo, gwf GatewayFactory) *PaymentService {
	return &PaymentService{
		paymentRepo:       pr,
		paymentMethodRepo: pmr,
		gwFactory:         gwf,
	}
}

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

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

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

	transaction := &entity.PaymentTransaction{
		PaymentID: payment.ID,
		Type:      entity.TransactionTypeRequest,
		Status:    entity.TransactionStatusPending,
		CreatedAt: time.Now(),
		UpdatedAt: time.Now(),
	}
	if err := s.paymentRepo.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 = entity.TransactionStatusFailed
		transaction.ErrorMessage = gwErr.Error()
		_ = s.paymentRepo.UpdateTransaction(ctx, transaction)
		return nil, fmt.Errorf("gateway request failed: %w", gwErr)
	}

	transaction.Status = entity.TransactionStatusSuccess
	transaction.GatewayToken = token
	if err := s.paymentRepo.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
}
