package gateway

import (
	"context"
	"strings"

	"github.com/tech/sendico/pkg/api/routers/gsresponse"
	"github.com/tech/sendico/pkg/merrors"
	"github.com/tech/sendico/pkg/mservice"
	mntxv1 "github.com/tech/sendico/pkg/proto/gateway/mntx/v1"
	"go.uber.org/zap"
	"google.golang.org/protobuf/proto"
)

func (s *Service) CreateCardPayout(ctx context.Context, req *mntxv1.CardPayoutRequest) (*mntxv1.CardPayoutResponse, error) {
	return executeUnary(ctx, s, "CreateCardPayout", s.handleCreateCardPayout, req)
}

func (s *Service) handleCreateCardPayout(ctx context.Context, req *mntxv1.CardPayoutRequest) gsresponse.Responder[mntxv1.CardPayoutResponse] {
	log := s.logger.Named("card_payout")
	log.Info("Create card payout request received",
		zap.String("payout_id", strings.TrimSpace(req.GetPayoutId())),
		zap.String("operation_ref", strings.TrimSpace(req.GetOperationRef())),
		zap.String("parent_payment_ref", strings.TrimSpace(req.GetParentPaymentRef())),
		zap.String("customer_id", strings.TrimSpace(req.GetCustomerId())),
		zap.Int64("amount_minor", req.GetAmountMinor()),
		zap.String("currency", strings.ToUpper(strings.TrimSpace(req.GetCurrency()))),
	)
	if s.card == nil {
		log.Warn("Card payout processor not initialised")
		return gsresponse.Internal[mntxv1.CardPayoutResponse](s.logger, mservice.MntxGateway, merrors.Internal("card payout processor not initialised"))
	}

	resp, err := s.card.Submit(ctx, req)
	if err != nil {
		log.Warn("Card payout submission failed", zap.Error(err))
		return gsresponse.Auto[mntxv1.CardPayoutResponse](s.logger, mservice.MntxGateway, err)
	}
	log.Info("Card payout submission completed", zap.String("payout_id", resp.GetPayout().GetPayoutId()), zap.Bool("accepted", resp.GetAccepted()))
	return gsresponse.Success(resp)
}

func (s *Service) CreateCardTokenPayout(ctx context.Context, req *mntxv1.CardTokenPayoutRequest) (*mntxv1.CardTokenPayoutResponse, error) {
	return executeUnary(ctx, s, "CreateCardTokenPayout", s.handleCreateCardTokenPayout, req)
}

func (s *Service) handleCreateCardTokenPayout(ctx context.Context, req *mntxv1.CardTokenPayoutRequest) gsresponse.Responder[mntxv1.CardTokenPayoutResponse] {
	log := s.logger.Named("card_token_payout")
	log.Info("Create card token payout request received",
		zap.String("payout_id", strings.TrimSpace(req.GetPayoutId())),
		zap.String("operation_ref", strings.TrimSpace(req.GetOperationRef())),
		zap.String("parent_payment_ref", strings.TrimSpace(req.GetParentPaymentRef())),
		zap.String("customer_id", strings.TrimSpace(req.GetCustomerId())),
		zap.Int64("amount_minor", req.GetAmountMinor()),
		zap.String("currency", strings.ToUpper(strings.TrimSpace(req.GetCurrency()))),
	)
	if s.card == nil {
		log.Warn("Card payout processor not initialised")
		return gsresponse.Internal[mntxv1.CardTokenPayoutResponse](s.logger, mservice.MntxGateway, merrors.Internal("card payout processor not initialised"))
	}

	resp, err := s.card.SubmitToken(ctx, req)
	if err != nil {
		log.Warn("Card token payout submission failed", zap.Error(err))
		return gsresponse.Auto[mntxv1.CardTokenPayoutResponse](s.logger, mservice.MntxGateway, err)
	}
	log.Info("Card token payout submission completed", zap.String("payout_id", resp.GetPayout().GetPayoutId()), zap.Bool("accepted", resp.GetAccepted()))
	return gsresponse.Success(resp)
}

func (s *Service) CreateCardToken(ctx context.Context, req *mntxv1.CardTokenizeRequest) (*mntxv1.CardTokenizeResponse, error) {
	return executeUnary(ctx, s, "CreateCardToken", s.handleCreateCardToken, req)
}

func (s *Service) handleCreateCardToken(ctx context.Context, req *mntxv1.CardTokenizeRequest) gsresponse.Responder[mntxv1.CardTokenizeResponse] {
	log := s.logger.Named("card_tokenize")
	log.Info("Create card token request received",
		zap.String("request_id", strings.TrimSpace(req.GetRequestId())),
		zap.String("customer_id", strings.TrimSpace(req.GetCustomerId())),
	)
	if s.card == nil {
		log.Warn("Card payout processor not initialised")
		return gsresponse.Internal[mntxv1.CardTokenizeResponse](s.logger, mservice.MntxGateway, merrors.Internal("card payout processor not initialised"))
	}

	resp, err := s.card.Tokenize(ctx, req)
	if err != nil {
		log.Warn("Card tokenization failed", zap.Error(err))
		return gsresponse.Auto[mntxv1.CardTokenizeResponse](s.logger, mservice.MntxGateway, err)
	}
	log.Info("Card tokenization completed", zap.String("request_id", resp.GetRequestId()), zap.Bool("success", resp.GetSuccess()))
	return gsresponse.Success(resp)
}

func (s *Service) GetCardPayoutStatus(ctx context.Context, req *mntxv1.GetCardPayoutStatusRequest) (*mntxv1.GetCardPayoutStatusResponse, error) {
	return executeUnary(ctx, s, "GetCardPayoutStatus", s.handleGetCardPayoutStatus, req)
}

func (s *Service) handleGetCardPayoutStatus(_ context.Context, req *mntxv1.GetCardPayoutStatusRequest) gsresponse.Responder[mntxv1.GetCardPayoutStatusResponse] {
	log := s.logger.Named("card_payout_status")
	log.Info("Get card payout status request received", zap.String("payout_id", strings.TrimSpace(req.GetPayoutId())))
	if s.card == nil {
		log.Warn("Card payout processor not initialised")
		return gsresponse.Internal[mntxv1.GetCardPayoutStatusResponse](s.logger, mservice.MntxGateway, merrors.Internal("card payout processor not initialised"))
	}

	state, err := s.card.Status(context.Background(), req.GetPayoutId())
	if err != nil {
		log.Warn("Card payout status lookup failed", zap.Error(err))
		return gsresponse.Auto[mntxv1.GetCardPayoutStatusResponse](s.logger, mservice.MntxGateway, err)
	}
	log.Info("Card payout status retrieved", zap.String("payout_id", state.GetPayoutId()), zap.String("status", state.GetStatus().String()))
	return gsresponse.Success(&mntxv1.GetCardPayoutStatusResponse{Payout: state})
}

func sanitizeCardPayoutRequest(req *mntxv1.CardPayoutRequest) *mntxv1.CardPayoutRequest {
	if req == nil {
		return nil
	}
	clean := proto.Clone(req)
	r, ok := clean.(*mntxv1.CardPayoutRequest)
	if !ok {
		return req
	}
	r.PayoutId = strings.TrimSpace(r.GetPayoutId())
	r.ParentPaymentRef = strings.TrimSpace(r.GetParentPaymentRef())
	r.CustomerId = strings.TrimSpace(r.GetCustomerId())
	r.CustomerFirstName = strings.TrimSpace(r.GetCustomerFirstName())
	r.CustomerMiddleName = strings.TrimSpace(r.GetCustomerMiddleName())
	r.CustomerLastName = strings.TrimSpace(r.GetCustomerLastName())
	r.CustomerIp = strings.TrimSpace(r.GetCustomerIp())
	r.CustomerZip = strings.TrimSpace(r.GetCustomerZip())
	r.CustomerCountry = strings.TrimSpace(r.GetCustomerCountry())
	r.CustomerState = strings.TrimSpace(r.GetCustomerState())
	r.CustomerCity = strings.TrimSpace(r.GetCustomerCity())
	r.CustomerAddress = strings.TrimSpace(r.GetCustomerAddress())
	r.Currency = strings.ToUpper(strings.TrimSpace(r.GetCurrency()))
	r.CardPan = strings.TrimSpace(r.GetCardPan())
	r.CardHolder = strings.TrimSpace(r.GetCardHolder())
	r.OperationRef = strings.TrimSpace(r.GetOperationRef())
	r.IdempotencyKey = strings.TrimSpace(r.GetIdempotencyKey())
	r.IntentRef = strings.TrimSpace(r.GetIntentRef())
	r.Comment = strings.TrimSpace(r.GetComment())
	return r
}

func sanitizeCardTokenPayoutRequest(req *mntxv1.CardTokenPayoutRequest) *mntxv1.CardTokenPayoutRequest {
	if req == nil {
		return nil
	}
	clean := proto.Clone(req)
	r, ok := clean.(*mntxv1.CardTokenPayoutRequest)
	if !ok {
		return req
	}
	r.PayoutId = strings.TrimSpace(r.GetPayoutId())
	r.ParentPaymentRef = strings.TrimSpace(r.GetParentPaymentRef())
	r.CustomerId = strings.TrimSpace(r.GetCustomerId())
	r.CustomerFirstName = strings.TrimSpace(r.GetCustomerFirstName())
	r.CustomerMiddleName = strings.TrimSpace(r.GetCustomerMiddleName())
	r.CustomerLastName = strings.TrimSpace(r.GetCustomerLastName())
	r.CustomerIp = strings.TrimSpace(r.GetCustomerIp())
	r.CustomerZip = strings.TrimSpace(r.GetCustomerZip())
	r.CustomerCountry = strings.TrimSpace(r.GetCustomerCountry())
	r.CustomerState = strings.TrimSpace(r.GetCustomerState())
	r.CustomerCity = strings.TrimSpace(r.GetCustomerCity())
	r.CustomerAddress = strings.TrimSpace(r.GetCustomerAddress())
	r.Currency = strings.ToUpper(strings.TrimSpace(r.GetCurrency()))
	r.CardToken = strings.TrimSpace(r.GetCardToken())
	r.CardHolder = strings.TrimSpace(r.GetCardHolder())
	r.MaskedPan = strings.TrimSpace(r.GetMaskedPan())
	r.OperationRef = strings.TrimSpace(r.GetOperationRef())
	r.IdempotencyKey = strings.TrimSpace(r.GetIdempotencyKey())
	r.IntentRef = strings.TrimSpace(r.GetIntentRef())
	r.Comment = strings.TrimSpace(r.GetComment())
	return r
}

func sanitizeCardTokenizeRequest(req *mntxv1.CardTokenizeRequest) *mntxv1.CardTokenizeRequest {
	if req == nil {
		return nil
	}
	clean := proto.Clone(req)
	r, ok := clean.(*mntxv1.CardTokenizeRequest)
	if !ok {
		return req
	}
	r.RequestId = strings.TrimSpace(r.GetRequestId())
	r.CustomerId = strings.TrimSpace(r.GetCustomerId())
	r.CustomerFirstName = strings.TrimSpace(r.GetCustomerFirstName())
	r.CustomerMiddleName = strings.TrimSpace(r.GetCustomerMiddleName())
	r.CustomerLastName = strings.TrimSpace(r.GetCustomerLastName())
	r.CustomerIp = strings.TrimSpace(r.GetCustomerIp())
	r.CustomerZip = strings.TrimSpace(r.GetCustomerZip())
	r.CustomerCountry = strings.TrimSpace(r.GetCustomerCountry())
	r.CustomerState = strings.TrimSpace(r.GetCustomerState())
	r.CustomerCity = strings.TrimSpace(r.GetCustomerCity())
	r.CustomerAddress = strings.TrimSpace(r.GetCustomerAddress())
	r.CardPan = strings.TrimSpace(r.GetCardPan())
	r.CardHolder = strings.TrimSpace(r.GetCardHolder())
	r.CardCvv = strings.TrimSpace(r.GetCardCvv())
	if card := r.GetCard(); card != nil {
		card.Pan = strings.TrimSpace(card.GetPan())
		card.CardHolder = strings.TrimSpace(card.GetCardHolder())
		card.Cvv = strings.TrimSpace(card.GetCvv())
		r.Card = card
	}
	return r
}
