package booking

import (
	"context"
	"errors"
	"log/slog"
	"math"
	"strings"

	"github.com/stripe/stripe-go/v83"
	"gorm.io/gorm"

	stripeclient "github.com/rjNemo/rentease/internal/driver/stripe"
)

// HandlePaymentIntentSucceeded persists successful Stripe payment intents received via webhook.
func (bs Service) HandlePaymentIntentSucceeded(ctx context.Context, pi *stripe.PaymentIntent) error {
	if pi == nil {
		return errors.New("payment intent payload is missing")
	}

	normalized := stripeclient.NormalizePaymentIntent(pi)
	if normalized.ID == "" {
		return errors.New("payment intent missing id")
	}

	if normalized.BookingID == nil {
		bs.logger.Warn("stripe webhook payment missing booking metadata", slog.String("payment_intent", normalized.ID))
		return nil
	}

	bookingID := uint(*normalized.BookingID)
	stripeID := normalized.ID
	status := strings.ToLower(normalized.Status)

	_, err := bs.store.UpsertStripePayment(&Payment{
		BookingID:       bookingID,
		Amount:          normalized.Amount,
		PaymentMethod:   mapStripeMethod(normalized.PaymentMethod),
		StripePaymentID: &stripeID,
		StripeStatus:    &status,
	})
	if err != nil {
		return err
	}

	bs.logger.Info("stripe payment intent processed", slog.String("payment_intent", normalized.ID), slog.Int("booking_id", int(bookingID)))
	return nil
}

// HandleChargeRefunded updates an existing Stripe payment when a charge is refunded.
func (bs Service) HandleChargeRefunded(ctx context.Context, ch *stripe.Charge) error {
	if ch == nil {
		return errors.New("charge payload is missing")
	}

	if ch.PaymentIntent == nil || ch.PaymentIntent.ID == "" {
		bs.logger.Warn("stripe refund missing payment intent", slog.String("charge", ch.ID))
		return nil
	}

	existing, err := bs.store.FindStripePayment(ch.PaymentIntent.ID)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			bs.logger.Warn("stripe refund received for unknown payment", slog.String("payment_intent", ch.PaymentIntent.ID))
			return nil
		}
		return err
	}

	amount := existing.Amount
	if ch.AmountRefunded > 0 {
		net := float64(ch.Amount-ch.AmountRefunded) / 100.0
		amount = math.Max(net, 0)
	}

	status := "refunded"
	stripeID := ch.PaymentIntent.ID

	_, err = bs.store.UpsertStripePayment(&Payment{
		BookingID:       existing.BookingID,
		Amount:          amount,
		PaymentMethod:   existing.PaymentMethod,
		StripePaymentID: &stripeID,
		StripeStatus:    &status,
	})
	if err != nil {
		return err
	}

	bs.logger.Info("stripe charge refunded processed", slog.String("charge", ch.ID), slog.String("payment_intent", ch.PaymentIntent.ID))
	return nil
}
