package com.saas.subscription.service;

import com.saas.subscription.entity.SubscriptionPlan;
import com.saas.subscription.entity.SubscriptionStatus;
import com.saas.subscription.entity.TenantSubscription;
import com.saas.subscription.repository.SubscriptionPlanRepository;
import com.saas.subscription.repository.SubscriptionPlanRepository;
import com.saas.subscription.repository.SubscriptionRepository;
import com.saas.subscription.repository.PaymentRecordRepository;
import com.saas.subscription.entity.PaymentRecord;
import com.stripe.model.Event;
import com.stripe.model.Invoice;
import com.stripe.model.StripeObject;
import com.stripe.model.Subscription;
import com.stripe.model.checkout.Session;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Optional;

@Service
@RequiredArgsConstructor
@Slf4j
public class StripeWebhookService {

    private final SubscriptionRepository subscriptionRepository;
    private final SubscriptionPlanRepository planRepository;
    private final PaymentRecordRepository paymentRecordRepository;

    @Transactional
    public void handleEvent(Event event) {
        log.info("Handling Stripe event: {}", event.getType());

        try {
            switch (event.getType()) {
                case "checkout.session.completed":
                    handleCheckoutSessionCompleted(event);
                    break;
                case "invoice.payment_succeeded":
                    handleInvoicePaymentSucceeded(event);
                    break;
                case "customer.subscription.deleted":
                    handleSubscriptionDeleted(event);
                    break;
                case "invoice.payment_failed":
                    handleInvoicePaymentFailed(event);
                    break;
                default:
                    log.debug("Unhandled event type: {}", event.getType());
            }
        } catch (Exception e) {
            log.error("Error handling Stripe event", e);
            // Don't throw exception to avoid Stripe retrying indefinitely for bad logic
        }
    }

    private void handleCheckoutSessionCompleted(Event event) {
        Session session = (Session) event.getDataObjectDeserializer().getObject().orElse(null);
        if (session == null)
            return;

        String tenantId = session.getMetadata().get("tenantId");
        String subscriptionId = session.getSubscription();
        String customerId = session.getCustomer();

        log.info("Checkout completed for tenant: {}, subscription: {}", tenantId, subscriptionId);

        try {
            // Fetch subscription details from Stripe to get the price/plan
            Subscription stripeSubscription = Subscription.retrieve(subscriptionId);
            String priceId = stripeSubscription.getItems().getData().get(0).getPrice().getId();
            long currentPeriodEnd = stripeSubscription.getCurrentPeriodEnd();

            // Find the plan by Stripe Price ID
            Optional<SubscriptionPlan> planOpt = planRepository.findByStripePriceId(priceId);

            subscriptionRepository.findByTenantId(tenantId).ifPresent(sub -> {
                sub.setStripeCustomerId(customerId);
                sub.setStripeSubscriptionId(subscriptionId);
                sub.setStatus(SubscriptionStatus.ACTIVE);
                sub.setCurrentPeriodEnd(
                        LocalDateTime.ofInstant(Instant.ofEpochSecond(currentPeriodEnd), ZoneId.systemDefault()));

                if (planOpt.isPresent()) {
                    sub.setPlan(planOpt.get());
                } else {
                    log.warn("Plan not found for Stripe Price ID: {}", priceId);
                }

                subscriptionRepository.save(sub);
            });
        } catch (Exception e) {
            log.error("Error fetching subscription details from Stripe", e);
        }
    }

    private void handleInvoicePaymentSucceeded(Event event) {
        Invoice invoice = (Invoice) event.getDataObjectDeserializer().getObject().orElse(null);
        if (invoice == null)
            return;

        String subscriptionId = invoice.getSubscription();
        if (subscriptionId == null)
            return;

        log.info("Payment succeeded for subscription: {}", subscriptionId);

        try {
            Subscription stripeSubscription = Subscription.retrieve(subscriptionId);
            long currentPeriodEnd = stripeSubscription.getCurrentPeriodEnd();

            subscriptionRepository.findByStripeSubscriptionId(subscriptionId).ifPresent(sub -> {
                sub.setStatus(SubscriptionStatus.ACTIVE);
                sub.setCurrentPeriodEnd(
                        LocalDateTime.ofInstant(Instant.ofEpochSecond(currentPeriodEnd), ZoneId.systemDefault()));
                subscriptionRepository.save(sub);

                // Save Payment Record
                PaymentRecord payment = PaymentRecord.builder()
                        .tenantId(sub.getTenantId())
                        .stripePaymentIntentId(invoice.getPaymentIntent())
                        .stripeInvoiceId(invoice.getId())
                        .amount(java.math.BigDecimal.valueOf(invoice.getAmountPaid())
                                .divide(java.math.BigDecimal.valueOf(100))) // Cents to Unit
                        .currency(invoice.getCurrency())
                        .status("succeeded")
                        .invoicePdfUrl(invoice.getInvoicePdf())
                        .description("Subscription Payment - " + sub.getPlan().getName())
                        .build();

                paymentRecordRepository.save(payment);
                log.info("Payment record saved for tenant: {}", sub.getTenantId());
            });
        } catch (Exception e) {
            log.error("Error updating subscription period after payment", e);
        }
    }

    private void handleSubscriptionDeleted(Event event) {
        Subscription subscription = (Subscription) event.getDataObjectDeserializer().getObject().orElse(null);
        if (subscription == null)
            return;

        log.info("Subscription deleted: {}", subscription.getId());

        subscriptionRepository.findByStripeSubscriptionId(subscription.getId()).ifPresent(sub -> {
            sub.setStatus(SubscriptionStatus.CANCELED);
            subscriptionRepository.save(sub);
        });
    }

    private void handleInvoicePaymentFailed(Event event) {
        Invoice invoice = (Invoice) event.getDataObjectDeserializer().getObject().orElse(null);
        if (invoice == null)
            return;

        String subscriptionId = invoice.getSubscription();
        if (subscriptionId == null)
            return;

        log.info("Payment failed for subscription: {}", subscriptionId);

        subscriptionRepository.findByStripeSubscriptionId(subscriptionId).ifPresent(sub -> {
            sub.setStatus(SubscriptionStatus.PAST_DUE);
            subscriptionRepository.save(sub);
        });
    }
}
