package payment

import (
	"context"
	"log"

	"go.uber.org/zap"
	"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
	"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
	"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/proto/treasurer"
)

type paymentClient interface {
	GetPaymentLinkBankCard(ctx context.Context, request *treasurer.GetPaymentLinkRequest) (string, errors.Error)
	GetPaymentLinkYooMoney(ctx context.Context, request *treasurer.GetPaymentLinkRequest) (string, errors.Error)
	GetPaymentLinkSberPay(ctx context.Context, request *treasurer.GetPaymentLinkRequest) (string, errors.Error)
	GetPaymentLinkTinkoff(ctx context.Context, request *treasurer.GetPaymentLinkRequest) (string, errors.Error)
	GetPaymentLinkSberbankB2B(ctx context.Context, request *treasurer.GetPaymentLinkRequest) (string, errors.Error)
}

type authClient interface {
	GetUser(ctx context.Context, userID string) (*models.User, errors.Error)
}

type Deps struct {
	Logger            *zap.Logger
	ConfigurationGRPC *models.ConfigurationGRPC
	PaymentClient     paymentClient
	AuthClient        authClient
}

type Service struct {
	logger            *zap.Logger
	configurationGRPC *models.ConfigurationGRPC
	paymentClient     paymentClient
	authClient        authClient
}

func New(deps Deps) *Service {
	if deps.Logger == nil {
		log.Panicln("logger is nil on <New (payment service)>")
	}

	if deps.ConfigurationGRPC == nil {
		log.Panicln("ConfigurationGRPC is nil on <New (payment service)>")
	}

	if deps.PaymentClient == nil {
		log.Panicln("PaymentClient is nil on <New (payment service)>")
	}

	if deps.AuthClient == nil {
		log.Panicln("AuthClient is nil on <New (payment service)>")
	}

	return &Service{
		logger:            deps.Logger,
		paymentClient:     deps.PaymentClient,
		authClient:        deps.AuthClient,
		configurationGRPC: deps.ConfigurationGRPC,
	}
}

func (receiver *Service) GetPaymentLink(ctx context.Context, request *models.GetPaymentLinkRequest) (string, errors.Error) {
	if _, userErr := receiver.authClient.GetUser(ctx, request.UserID); userErr != nil {
		receiver.logger.Error("failed to get user on <GetPaymentLink> on <PaymentService>",
			zap.Error(userErr),
			zap.String("userID", request.UserID),
		)

		return "", userErr
	}

	switch request.Body.Type {
	case models.PaymentTypeBankCard:
		return receiver.GetPaymentLinkBankCard(ctx, request)
	case models.PaymentTypeYoomoney:
		return receiver.GetPaymentLinkYooMoney(ctx, request)
	case models.PaymentTypeSberPay:
		return receiver.GetPaymentLinkSberPay(ctx, request)
	case models.PaymentTypeTinkoff:
		return receiver.GetPaymentLinkTinkoff(ctx, request)
	}

	return "", errors.NewWithMessage("invalid payment method type", errors.ErrInvalidArgs)
}

func (receiver *Service) GetPaymentLinkBankCard(ctx context.Context, request *models.GetPaymentLinkRequest) (string, errors.Error) {
	link, err := receiver.paymentClient.GetPaymentLinkBankCard(ctx, &treasurer.GetPaymentLinkRequest{
		MainSettings: &treasurer.MainPaymentSettings{
			Currency:         request.Body.Currency,
			Amount:           request.Body.Amount,
			UserID:           request.UserID,
			ClientIP:         request.ClientIP,
			CallbackHostGRPC: []string{receiver.configurationGRPC.Domen},
			ReturnURL:        request.Body.ReturnURL,
		},
	})
	if err != nil {
		receiver.logger.Error("failed to get bankcard payment link on <GetPaymentLinkBankCard> of <PaymentService>", zap.Error(err))
		return "", err
	}

	return link, nil
}

func (receiver *Service) GetPaymentLinkYooMoney(ctx context.Context, request *models.GetPaymentLinkRequest) (string, errors.Error) {
	link, err := receiver.paymentClient.GetPaymentLinkYooMoney(ctx, &treasurer.GetPaymentLinkRequest{
		MainSettings: &treasurer.MainPaymentSettings{
			Currency:         request.Body.Currency,
			Amount:           request.Body.Amount,
			UserID:           request.UserID,
			ClientIP:         request.ClientIP,
			CallbackHostGRPC: []string{receiver.configurationGRPC.Domen},
			ReturnURL:        request.Body.ReturnURL,
		},
	})
	if err != nil {
		receiver.logger.Error("failed to get yoomoney payment link on <GetPaymentLinkYooMoney> of <PaymentService>", zap.Error(err))
		return "", err
	}

	return link, nil
}

func (receiver *Service) GetPaymentLinkSberPay(ctx context.Context, request *models.GetPaymentLinkRequest) (string, errors.Error) {
	link, err := receiver.paymentClient.GetPaymentLinkSberPay(ctx, &treasurer.GetPaymentLinkRequest{
		MainSettings: &treasurer.MainPaymentSettings{
			Currency:         request.Body.Currency,
			Amount:           request.Body.Amount,
			UserID:           request.UserID,
			ClientIP:         request.ClientIP,
			CallbackHostGRPC: []string{receiver.configurationGRPC.Domen},
			ReturnURL:        request.Body.ReturnURL,
		},
	})
	if err != nil {
		receiver.logger.Error("failed to get sberpay payment link on <GetPaymentLinkSberPay> of <PaymentService>", zap.Error(err))
		return "", err
	}

	return link, nil
}

func (receiver *Service) GetPaymentLinkTinkoff(ctx context.Context, request *models.GetPaymentLinkRequest) (string, errors.Error) {
	link, err := receiver.paymentClient.GetPaymentLinkTinkoff(ctx, &treasurer.GetPaymentLinkRequest{
		MainSettings: &treasurer.MainPaymentSettings{
			Currency:         request.Body.Currency,
			Amount:           request.Body.Amount,
			UserID:           request.UserID,
			ClientIP:         request.ClientIP,
			CallbackHostGRPC: []string{receiver.configurationGRPC.Domen},
			ReturnURL:        request.Body.ReturnURL,
		},
	})
	if err != nil {
		receiver.logger.Error("failed to get tinkoff payment link on <GetPaymentLinkTinkoff> of <PaymentService>", zap.Error(err))
		return "", err
	}

	return link, nil
}
