package com.petshop.backend.service;

import com.petshop.backend.dto.cart.*;
import com.petshop.backend.entity.*;
import com.petshop.backend.exception.BusinessException;
import com.petshop.backend.exception.ResourceNotFoundException;
import com.petshop.backend.repository.*;
import com.stripe.Stripe;
import com.stripe.exception.StripeException;
import com.stripe.model.PaymentIntent;
import com.stripe.param.PaymentIntentCreateParams;
import jakarta.annotation.PostConstruct;
import jakarta.transaction.Transactional;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.List;

@Service
public class CartService {

    private final CartRepository cartRepository;
    private final CartItemRepository cartItemRepository;
    private final UserRepository userRepository;
    private final StoreRepository storeRepository;
    private final ProductRepository productRepository;
    private final CouponRepository couponRepository;

    @Value("${stripe.secret-key:}")
    private String stripeSecretKey;

    public CartService(CartRepository cartRepository,
                       CartItemRepository cartItemRepository,
                       UserRepository userRepository,
                       StoreRepository storeRepository,
                       ProductRepository productRepository,
                       CouponRepository couponRepository) {
        this.cartRepository = cartRepository;
        this.cartItemRepository = cartItemRepository;
        this.userRepository = userRepository;
        this.storeRepository = storeRepository;
        this.productRepository = productRepository;
        this.couponRepository = couponRepository;
    }

    @PostConstruct
    public void initStripe() {
        if (stripeSecretKey != null && !stripeSecretKey.isBlank()) {
            Stripe.apiKey = stripeSecretKey;
        }
    }

    public CartResponse getActiveCart(Long userId, Long storeId) {
        return cartRepository
                .findActiveCartByUserAndStore(userId, storeId, "ACTIVE")
                .map(this::toResponse)
                .orElse(null);
    }

    @Transactional
    public CartResponse addItem(Long userId, AddToCartRequest request) {
        User user = userRepository.findById(userId).orElseThrow(() -> new ResourceNotFoundException("User not found"));
        StoreLocation store = storeRepository.findById(request.getStoreId()).orElseThrow(() -> new ResourceNotFoundException("Store not found"));
        Product product = productRepository.findById(request.getProdId()).orElseThrow(() -> new ResourceNotFoundException("Product not found"));

        if (request.getQuantity() < 1) {
            throw new BusinessException("Quantity must be at least 1");
        }

        Cart cart = cartRepository
                .findActiveCartByUserAndStore(userId, request.getStoreId(), "ACTIVE")
                .orElseGet(() -> {
                    Cart newCart = new Cart();
                    newCart.setUser(user);
                    newCart.setStore(store);
                    newCart.setCartStatus("ACTIVE");

                    return cartRepository.save(newCart);
                });

        cartItemRepository.findByCartCartIdAndProductProdId(cart.getCartId(), product.getProdId())
                .ifPresentOrElse(
                        existing -> existing.setQuantity(existing.getQuantity() + request.getQuantity()),
                        () -> {
                            CartItem item = new CartItem();
                            item.setCart(cart);
                            item.setProduct(product);
                            item.setQuantity(request.getQuantity());
                            item.setUnitPrice(product.getProdPrice());
                            cartItemRepository.save(item);
                        }
                );

        recalculate(cart);

        return toResponse(cart);
    }

    @Transactional
    public CartResponse updateItem(Long userId, UpdateCartItemRequest request) {
        CartItem item = cartItemRepository.findById(request.getCartItemId())
                .orElseThrow(() -> new ResourceNotFoundException("Cart item not found"));

        if (!item.getCart().getUser().getId().equals(userId)) {
            throw new BusinessException("Access denied");
        }

        if (!item.getCart().getCartStatus().equals("ACTIVE")) {
            throw new BusinessException("Cart is not active");
        }

        if (request.getQuantity() < 1) {
            throw new BusinessException("Quantity must be at least 1");
        }

        item.setQuantity(request.getQuantity());
        cartItemRepository.save(item);
        recalculate(item.getCart());

        return toResponse(item.getCart());
    }

    @Transactional
    public CartResponse removeItem(Long userId, Long cartItemId) {
        CartItem item = cartItemRepository.findById(cartItemId)
                .orElseThrow(() -> new ResourceNotFoundException("Cart item not found"));

        if (!item.getCart().getUser().getId().equals(userId)) {
            throw new BusinessException("Access denied");
        }

        if (!item.getCart().getCartStatus().equals("ACTIVE")) {
            throw new BusinessException("Cart is not active");
        }

        Cart cart = item.getCart();
        cartItemRepository.delete(item);
        recalculate(cart);

        return toResponse(cart);
    }

    @Transactional
    public void clearCart(Long userId, Long storeId) {
        cartRepository.findActiveCartByUserAndStore(userId, storeId, "ACTIVE")
                .ifPresent(cart -> {
                    cartItemRepository.deleteByCartCartId(cart.getCartId());
                    cart.setSubtotalAmount(BigDecimal.ZERO);
                    cart.setDiscountAmount(BigDecimal.ZERO);
                    cart.setTotalAmount(BigDecimal.ZERO);
                    cart.setCoupon(null);
                    cartRepository.save(cart);
                });
    }

    @Transactional
    public CartResponse applyCoupon(Long userId, Long storeId, String couponCode) {
        Cart cart = cartRepository
                .findActiveCartByUserAndStore(userId, storeId, "ACTIVE")
                .orElseThrow(() -> new BusinessException("No active cart found"));

        Coupon coupon = couponRepository.findByCouponCodeIgnoreCase(couponCode)
                .orElseThrow(() -> new BusinessException("Invalid coupon code"));

        LocalDateTime now = LocalDateTime.now();

        if (!coupon.getActive()) {
            throw new BusinessException("Coupon is no longer active");
        }

        if (coupon.getStartsAt() != null && now.isBefore(coupon.getStartsAt())) {
            throw new BusinessException("Coupon is not yet valid");
        }

        if (coupon.getEndsAt() != null && now.isAfter(coupon.getEndsAt())) {
            throw new BusinessException("Coupon has expired");
        }

        if (coupon.getMinOrderAmount() != null && cart.getSubtotalAmount().compareTo(coupon.getMinOrderAmount()) < 0) {
            throw new BusinessException("Minimum order amount of $" + coupon.getMinOrderAmount() + " required");
        }

        cart.setCoupon(coupon);
        recalculate(cart);

        return toResponse(cart);
    }

    @Transactional
    public CheckoutResponse checkout(Long userId, Long storeId) {
        Cart cart = cartRepository
                .findActiveCartByUserAndStore(userId, storeId, "ACTIVE")
                .orElseThrow(() -> new BusinessException("No active cart found"));

        List<CartItem> items = cartItemRepository.findByCartCartId(cart.getCartId());
        if (items.isEmpty()) {
            throw new BusinessException("Cart is empty");
        }

        long amountInCents = cart.getTotalAmount()
                .multiply(BigDecimal.valueOf(100))
                .setScale(0, RoundingMode.HALF_UP)
                .longValue();

        if (amountInCents < 50) {
            throw new BusinessException("Order total is too low to process payment");
        }

        try {
            PaymentIntentCreateParams params = PaymentIntentCreateParams.builder()
                    .setAmount(amountInCents)
                    .setCurrency("usd")
                    .putMetadata("cartId", String.valueOf(cart.getCartId()))
                    .putMetadata("userId", String.valueOf(userId))
                    .build();

            PaymentIntent intent = PaymentIntent.create(params);

            return new CheckoutResponse(
                    cart.getCartId(),
                    intent.getClientSecret(),
                    cart.getTotalAmount(),
                    intent.getStatus()
            );

        }

        catch (StripeException e) {
            throw new BusinessException("Payment processing failed: " + e.getMessage());
        }
    }

    @Transactional
    public void completeCheckout(Long userId, String paymentIntentId) {
        try {
            PaymentIntent intent = PaymentIntent.retrieve(paymentIntentId);

            if (!"succeeded".equals(intent.getStatus())) {
                throw new BusinessException("Payment has not been completed");
            }

            Long cartId = Long.parseLong(intent.getMetadata().get("cartId"));
            Cart cart = cartRepository.findById(cartId)
                    .orElseThrow(() -> new BusinessException("Cart not found"));

            if (!cart.getUser().getId().equals(userId)) {
                throw new BusinessException("Unauthorized");
            }

            cart.setCartStatus("CHECKED_OUT");
            cartRepository.save(cart);

        } catch (StripeException e) {
            throw new BusinessException("Payment verification failed: " + e.getMessage());
        }
    }

    private void recalculate(Cart cart) {
        List<CartItem> items = cartItemRepository.findByCartCartId(cart.getCartId());

        BigDecimal subtotal = items.stream()
                .map(i -> i.getUnitPrice().multiply(BigDecimal.valueOf(i.getQuantity())))
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        cart.setSubtotalAmount(subtotal);

        BigDecimal discount = BigDecimal.ZERO;
        Coupon coupon = cart.getCoupon();

        if (coupon != null) {
            if ("PERCENTAGE".equalsIgnoreCase(coupon.getDiscountType())) {
                discount = subtotal.multiply(coupon.getDiscountValue())
                        .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
            } 
            
            else if ("FIXED".equalsIgnoreCase(coupon.getDiscountType())) {
                discount = coupon.getDiscountValue().min(subtotal);
            }
        }

        discount = discount.max(BigDecimal.ZERO).min(subtotal);
        cart.setDiscountAmount(discount);
        cart.setTotalAmount(subtotal.subtract(discount).max(BigDecimal.ZERO));
        cartRepository.save(cart);
    }

    private CartResponse toResponse(Cart cart) {
        List<CartItemResponse> itemResponses = cartItemRepository
                .findByCartCartId(cart.getCartId())
                .stream()
                .map(item -> new CartItemResponse(
                        item.getCartItemId(),
                        item.getProduct().getProdId(),
                        item.getProduct().getProdName(),
                        item.getProduct().getImageUrl(),
                        item.getUnitPrice(),
                        item.getQuantity(),
                        item.getUnitPrice().multiply(BigDecimal.valueOf(item.getQuantity()))
                ))
                .toList();

        CartResponse response = new CartResponse();
        response.setCartId(cart.getCartId());
        response.setStoreId(cart.getStore() != null ? cart.getStore().getStoreId() : null);
        response.setCartStatus(cart.getCartStatus());
        response.setItems(itemResponses);
        response.setSubtotalAmount(cart.getSubtotalAmount());
        response.setDiscountAmount(cart.getDiscountAmount());
        response.setTotalAmount(cart.getTotalAmount());
        response.setCouponCode(cart.getCoupon() != null ? cart.getCoupon().getCouponCode() : null);
        
        return response;
    }
}
