package com.emonster.taroaichat.service.stripe;

import com.emonster.taroaichat.config.ApplicationProperties;
import com.emonster.taroaichat.domain.UserProfile;
import com.emonster.taroaichat.web.rest.errors.BadRequestAlertException;
import com.stripe.Stripe;
import com.stripe.exception.StripeException;
import com.stripe.model.PaymentIntent;
import com.stripe.model.Customer;
import com.stripe.model.PaymentMethod;
import com.stripe.model.Refund;
import com.stripe.param.PaymentIntentCreateParams;
import com.stripe.param.CustomerCreateParams;
import com.stripe.param.PaymentMethodAttachParams;
import com.stripe.param.RefundCreateParams;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;

/**
 * Service for handling Stripe payment processing for donations.
 */
@Service
public class StripeService {

    private static final Logger LOG = LoggerFactory.getLogger(StripeService.class);

    private final ApplicationProperties applicationProperties;

    public StripeService(ApplicationProperties applicationProperties) {
        this.applicationProperties = applicationProperties;
    }

    /**
     * Create a Stripe Customer for a donation user.
     *
     * @param userProfile the user profile entity
     * @return the Stripe customer ID
     */
    public String createDonationCustomer(UserProfile userProfile) {
        try {
            // Set Stripe API key
            Stripe.apiKey = applicationProperties.getStripe().getMerchantSecret();

            // Create customer parameters for donation
            CustomerCreateParams params = CustomerCreateParams.builder()
                .setPhone(userProfile.getPhone())
                .putMetadata("user_profile_id", userProfile.getId().toString())
                .putMetadata("phone", userProfile.getPhone())
                .setDescription("Tarot AI Chat - Donation Customer")
                .build();

            Customer stripeCustomer = Customer.create(params);

            LOG.debug("Stripe donation customer created: {} for user profile: {}", stripeCustomer.getId(), userProfile.getId());

            return stripeCustomer.getId();

        } catch (StripeException e) {
            LOG.error("Stripe donation customer creation failed for user profile: {}. Error: {}", userProfile.getId(), e.getMessage(), e);
            throw new BadRequestAlertException(
                String.format("Failed to create Stripe donation customer: %s", e.getMessage()),
                "Donation",
                "STRIPE_DONATION_CUSTOMER_CREATION_FAILED"
            );
        }
    }

    /**
     * Attach a payment method to a Stripe customer.
     *
     * @param stripePaymentMethodId the Stripe payment method ID
     * @param stripeCustomerId the Stripe customer ID
     * @return the attached payment method
     */
    public String attachPaymentMethodToCustomer(String stripePaymentMethodId, String stripeCustomerId) {
        try {
            // Set Stripe API key
            Stripe.apiKey = applicationProperties.getStripe().getMerchantSecret();

            // Retrieve the payment method
            PaymentMethod paymentMethod = PaymentMethod.retrieve(stripePaymentMethodId);

            // Attach to customer
            PaymentMethodAttachParams params = PaymentMethodAttachParams.builder()
                .setCustomer(stripeCustomerId)
                .build();

            paymentMethod.attach(params);

            LOG.debug("Payment method {} attached to customer {}", stripePaymentMethodId, stripeCustomerId);

            return paymentMethod.getId();

        } catch (StripeException e) {
            LOG.error("Failed to attach payment method {} to customer {}. Error: {}",
                stripePaymentMethodId, stripeCustomerId, e.getMessage(), e);
            throw new BadRequestAlertException(e.getMessage(), "Donation", "STRIPE_PAYMENT_METHOD_ATTACH_FAILED");
        }
    }

    /**
     * Create a PaymentIntent for a donation.
     *
     * @param userProfile the user making the donation
     * @param amount the donation amount
     * @param currency the currency code (default USD)
     * @param sessionId optional tarot session ID for context
     * @return the PaymentIntent client secret
     */
    public String createDonationPaymentIntent(UserProfile userProfile, BigDecimal amount, String currency, Long sessionId) {
        try {
            // Set Stripe API key
            Stripe.apiKey = applicationProperties.getStripe().getMerchantSecret();

            // Convert amount to cents
            long amountInCents = amount.multiply(new BigDecimal("100")).longValue();
            String currencyCode = currency != null ? currency.toLowerCase() : "usd";

            // Create customer first if needed
            String stripeCustomerId = createDonationCustomer(userProfile);

            PaymentIntentCreateParams.Builder paramsBuilder = PaymentIntentCreateParams.builder()
                .setAmount(amountInCents)
                .setCurrency(currencyCode)
                .setCustomer(stripeCustomerId)
                .setConfirmationMethod(PaymentIntentCreateParams.ConfirmationMethod.AUTOMATIC)
                .setConfirm(false) // Let frontend handle confirmation
                .addPaymentMethodType("card") // Only allow card payments
                .putMetadata("payment_type", "donation")
                .putMetadata("user_profile_id", userProfile.getId().toString())
                .putMetadata("phone", userProfile.getPhone())
                .setDescription("Tarot AI Chat - Donation/Tip");

            // Add session context if provided
            if (sessionId != null) {
                paramsBuilder.putMetadata("session_id", sessionId.toString());
            }

            PaymentIntent paymentIntent = PaymentIntent.create(paramsBuilder.build());

            LOG.debug("Donation PaymentIntent created: {} for user: {} amount: {} {}",
                paymentIntent.getId(), userProfile.getId(), amount, currencyCode);

            return paymentIntent.getClientSecret();

        } catch (StripeException e) {
            LOG.error("Donation PaymentIntent creation failed for user: {} amount: {}. Error: {}",
                userProfile.getId(), amount, e.getMessage(), e);
            throw new BadRequestAlertException(
                String.format("Failed to create donation payment intent: %s", e.getMessage()),
                "Donation",
                "STRIPE_DONATION_PAYMENT_INTENT_CREATION_FAILED"
            );
        }
    }

    /**
     * Get PaymentIntent by ID and validate it belongs to the user.
     *
     * @param paymentIntentId the Stripe PaymentIntent ID
     * @param userProfileId the user profile ID to validate against
     * @return the PaymentIntent
     */
    public PaymentIntent getPaymentIntent(String paymentIntentId, Long userProfileId) {
        try {
            // Set Stripe API key
            Stripe.apiKey = applicationProperties.getStripe().getMerchantSecret();

            PaymentIntent paymentIntent = PaymentIntent.retrieve(paymentIntentId);

            // Validate that this payment intent belongs to the user
            String intentUserProfileId = paymentIntent.getMetadata().get("user_profile_id");
            if (!userProfileId.toString().equals(intentUserProfileId)) {
                LOG.warn("Payment intent ownership validation failed. Expected userId: {}, Found in metadata: {}", 
                         userProfileId, intentUserProfileId);
                throw new BadRequestAlertException(
                    "Payment intent does not belong to this user",
                    "Donation",
                    "UNAUTHORIZED_PAYMENT_INTENT_ACCESS"
                );
            }

            return paymentIntent;

        } catch (StripeException e) {
            LOG.error("Failed to retrieve PaymentIntent: {}. Error: {}", paymentIntentId, e.getMessage(), e);
            throw new BadRequestAlertException(
                String.format("Failed to retrieve payment intent: %s", e.getMessage()),
                "Donation",
                "STRIPE_PAYMENT_INTENT_RETRIEVAL_FAILED"
            );
        }
    }

    /**
     * Refund a donation payment.
     *
     * @param paymentIntentId the Stripe PaymentIntent ID to refund
     * @param amount optional partial refund amount, null for full refund
     * @param reason the refund reason
     * @return the Refund object
     */
    public Refund refundDonation(String paymentIntentId, BigDecimal amount, String reason) {
        try {
            // Set Stripe API key
            Stripe.apiKey = applicationProperties.getStripe().getMerchantSecret();

            RefundCreateParams.Builder paramsBuilder = RefundCreateParams.builder()
                .setPaymentIntent(paymentIntentId)
                .setReason(RefundCreateParams.Reason.REQUESTED_BY_CUSTOMER);

            if (amount != null) {
                long amountInCents = amount.multiply(new BigDecimal("100")).longValue();
                paramsBuilder.setAmount(amountInCents);
            }

            if (reason != null) {
                paramsBuilder.putMetadata("reason", reason);
            }

            Refund refund = Refund.create(paramsBuilder.build());

            LOG.debug("Donation refund created: {} for PaymentIntent: {} amount: {}",
                refund.getId(), paymentIntentId, amount);

            return refund;

        } catch (StripeException e) {
            LOG.error("Donation refund failed for PaymentIntent: {}. Error: {}", paymentIntentId, e.getMessage(), e);
            throw new BadRequestAlertException(
                String.format("Failed to process donation refund: %s", e.getMessage()),
                "Donation",
                "STRIPE_DONATION_REFUND_FAILED"
            );
        }
    }
}
