package com.rit.canteen.sales.controller;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.rit.canteen.sales.model.PaymentSession;
import com.rit.canteen.sales.repository.PaymentSessionRepository;
import com.rit.canteen.sales.service.PaymentService;
import com.rit.canteen.sales.service.RazorpayService;
import io.jsonwebtoken.Claims;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/api/payments")
public class PaymentController {

    private static final Logger log = LoggerFactory.getLogger(PaymentController.class);

    @Autowired
    private PaymentService paymentService;

    @Autowired
    private RazorpayService razorpayService;

    @Autowired
    private PaymentSessionRepository paymentSessionRepository;

    private final ObjectMapper objectMapper = new ObjectMapper();

    /**
     * Create a Razorpay order for either food checkout or wallet top-up.
     *
     * Body for wallet:
     *   { "purpose": "WALLET_TOPUP", "amount": 100 }
     *
     * Body for food order:
     *   { "purpose": "ORDER", "order": { userId, items, totalAmount, orderType } }
     */
    @PostMapping("/create")
    public ResponseEntity<?> createPayment(@RequestBody Map<String, Object> body) {
        try {
            Long userId = requireUserId();
            String purpose = body.get("purpose") != null ? body.get("purpose").toString().toUpperCase() : "";

            if ("WALLET_TOPUP".equals(purpose)) {
                if (body.get("amount") == null) {
                    return ResponseEntity.badRequest().body(Map.of("success", false, "message", "amount is required"));
                }
                BigDecimal amount = new BigDecimal(body.get("amount").toString());
                Map<String, Object> result = paymentService.createWalletTopupSession(userId, amount);
                return ResponseEntity.ok(result);
            }

            if ("ORDER".equals(purpose)) {
                @SuppressWarnings("unchecked")
                Map<String, Object> order = body.get("order") instanceof Map
                    ? (Map<String, Object>) body.get("order")
                    : body; // allow flat body for convenience

                // Force userId from JWT — never trust client for identity
                order.put("userId", userId);
                Map<String, Object> result = paymentService.createOrderPaymentSession(userId, order);
                if (Boolean.FALSE.equals(result.get("success"))) {
                    return ResponseEntity.badRequest().body(result);
                }
                return ResponseEntity.ok(result);
            }

            return ResponseEntity.badRequest().body(Map.of(
                "success", false,
                "message", "purpose must be ORDER or WALLET_TOPUP"
            ));
        } catch (IllegalArgumentException e) {
            return ResponseEntity.badRequest().body(Map.of("success", false, "message", e.getMessage()));
        } catch (IllegalStateException e) {
            return ResponseEntity.status(503).body(Map.of("success", false, "message", e.getMessage()));
        } catch (SecurityException e) {
            return ResponseEntity.status(403).body(Map.of("success", false, "message", e.getMessage()));
        } catch (Exception e) {
            log.error("Create payment failed", e);
            return ResponseEntity.status(500).body(Map.of(
                "success", false,
                "message", e.getMessage() != null ? e.getMessage() : "Failed to create payment"
            ));
        }
    }

    /**
     * Verify Razorpay checkout response and fulfill (place order / credit wallet).
     * Only after this succeeds should the client show the order QR.
     */
    @PostMapping("/verify")
    public ResponseEntity<?> verifyPayment(@RequestBody Map<String, Object> body) {
        try {
            Long userId = requireUserId();
            String orderId = str(body.get("razorpayOrderId"), body.get("razorpay_order_id"));
            String paymentId = str(body.get("razorpayPaymentId"), body.get("razorpay_payment_id"));
            String signature = str(body.get("razorpaySignature"), body.get("razorpay_signature"));

            Map<String, Object> result = paymentService.verifyAndFulfill(userId, orderId, paymentId, signature);
            return ResponseEntity.ok(result);
        } catch (IllegalArgumentException e) {
            return ResponseEntity.badRequest().body(Map.of("success", false, "message", e.getMessage()));
        } catch (SecurityException e) {
            return ResponseEntity.status(403).body(Map.of("success", false, "message", e.getMessage()));
        } catch (IllegalStateException e) {
            return ResponseEntity.status(409).body(Map.of("success", false, "message", e.getMessage()));
        } catch (Exception e) {
            log.error("Verify payment failed", e);
            return ResponseEntity.status(500).body(Map.of(
                "success", false,
                "message", e.getMessage() != null ? e.getMessage() : "Payment verification failed"
            ));
        }
    }

    /**
     * Public webhook endpoint. Configure in Razorpay Dashboard:
     *   URL: https://<your-host>/api/payments/webhook
     *   Events: payment.captured
     * Set RAZORPAY_WEBHOOK_SECRET to the dashboard secret.
     */
    @PostMapping("/webhook")
    public ResponseEntity<?> webhook(
            @RequestBody String rawBody,
            @RequestHeader(value = "X-Razorpay-Signature", required = false) String signature) {
        try {
            if (razorpayService.hasWebhookSecret()) {
                if (signature == null || !razorpayService.verifyWebhookSignature(rawBody, signature)) {
                    log.warn("Rejected Razorpay webhook with invalid signature");
                    return ResponseEntity.status(400).body(Map.of("error", "Invalid signature"));
                }
            } else {
                log.warn("Webhook received but RAZORPAY_WEBHOOK_SECRET is not set — processing cautiously");
            }

            JsonNode root = objectMapper.readTree(rawBody);
            String event = root.path("event").asText("");
            if ("payment.captured".equals(event) || "payment.authorized".equals(event)) {
                JsonNode entity = root.path("payload").path("payment").path("entity");
                String paymentId = entity.path("id").asText(null);
                String orderId = entity.path("order_id").asText(null);
                if (paymentId != null && orderId != null) {
                    paymentService.handlePaymentCapturedWebhook(orderId, paymentId);
                }
            }
            // Always 200 so Razorpay does not retry endlessly on unknown events
            return ResponseEntity.ok(Map.of("received", true));
        } catch (Exception e) {
            log.error("Webhook processing error", e);
            // Return 200 to avoid noisy retries for parse errors; log for ops
            return ResponseEntity.ok(Map.of("received", true, "error", e.getMessage()));
        }
    }

    /**
     * Returns the payment history for the authenticated user.
     */
    @GetMapping("/history")
    public ResponseEntity<?> paymentHistory() {
        try {
            Long userId = requireUserId();
            List<PaymentSession> sessions = paymentSessionRepository.findByUserIdOrderByCreatedAtDesc(userId);
            List<Map<String, Object>> list = sessions.stream().map(s -> {
                Map<String, Object> m = new LinkedHashMap<>();
                m.put("id", s.getId());
                m.put("purpose", s.getPurpose().name());
                m.put("status", s.getStatus().name());
                m.put("amount", s.getAmountInr());
                m.put("razorpayOrderId", s.getRazorpayOrderId());
                m.put("razorpayPaymentId", s.getRazorpayPaymentId());
                m.put("orderNumber", s.getFulfillmentOrderNumber());
                m.put("createdAt", s.getCreatedAt());
                m.put("fulfilledAt", s.getFulfilledAt());
                m.put("failureReason", s.getFailureReason());
                return m;
            }).toList();
            return ResponseEntity.ok(list);
        } catch (SecurityException e) {
            return ResponseEntity.status(403).body(Map.of("success", false, "message", e.getMessage()));
        } catch (Exception e) {
            log.error("Payment history failed", e);
            return ResponseEntity.status(500).body(Map.of("success", false, "message", "Failed to fetch payment history"));
        }
    }

    /** Health/config check for the ordering app (does not expose secret). */
    @GetMapping("/config")
    public ResponseEntity<?> config() {
        Map<String, Object> resp = new LinkedHashMap<>();
        resp.put("enabled", razorpayService.isConfigured());
        if (razorpayService.isConfigured()) {
            resp.put("keyId", razorpayService.getKeyId());
        }
        return ResponseEntity.ok(resp);
    }

    private Long requireUserId() {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (auth != null && auth.getDetails() instanceof Claims claims) {
            Object uid = claims.get("userId");
            if (uid != null) {
                return uid instanceof Integer ? ((Integer) uid).longValue() : (Long) uid;
            }
        }
        throw new SecurityException("Authentication required");
    }

    private static String str(Object primary, Object fallback) {
        if (primary != null && !primary.toString().isBlank()) return primary.toString();
        if (fallback != null && !fallback.toString().isBlank()) return fallback.toString();
        return null;
    }
}
