/**
 * Grubtech Order Receive Webhook - Java (Spring Boot)
 *
 * This example demonstrates how to receive orders from Grubtech
 * via webhook and process them in your system.
 *
 * Prerequisites:
 * - Java 17+
 * - Spring Boot 3.x
 *
 * Replace the following placeholders:
 * - {{WEBHOOK_SECRET}}: Your webhook secret for signature verification
 * - {{ORDER_PROCESSING_LOGIC}}: Your business logic to process the order
 */

package com.example.grubtech;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Map;

@SpringBootApplication
@RestController
@RequestMapping("/webhooks/grubtech")
public class GrubtechOrderWebhook {

    private static final String WEBHOOK_SECRET = "{{WEBHOOK_SECRET}}";

    /**
     * Verify webhook signature using HMAC-SHA256
     */
    private boolean verifySignature(String body, String signature) {
        if (signature == null || signature.isEmpty()) {
            System.err.println("❌ No signature provided");
            return false;
        }

        try {
            Mac sha256Hmac = Mac.getInstance("HmacSHA256");
            SecretKeySpec secretKey = new SecretKeySpec(
                    WEBHOOK_SECRET.getBytes(StandardCharsets.UTF_8),
                    "HmacSHA256"
            );
            sha256Hmac.init(secretKey);

            byte[] hash = sha256Hmac.doFinal(body.getBytes(StandardCharsets.UTF_8));
            String expectedSignature = bytesToHex(hash);

            // Constant-time comparison
            boolean isValid = MessageDigest.isEqual(
                    signature.getBytes(StandardCharsets.UTF_8),
                    expectedSignature.getBytes(StandardCharsets.UTF_8)
            );

            if (!isValid) {
                System.err.println("❌ Invalid signature");
            }

            return isValid;

        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
            System.err.println("❌ Signature verification error: " + e.getMessage());
            return false;
        }
    }

    /**
     * Convert byte array to hex string
     */
    private String bytesToHex(byte[] bytes) {
        StringBuilder result = new StringBuilder();
        for (byte b : bytes) {
            result.append(String.format("%02x", b));
        }
        return result.toString();
    }

    /**
     * Process order (replace with your business logic)
     */
    private WebhookResponse processOrder(Order order) {
        try {
            System.out.println("📦 Processing order: " + order.getOrderId());
            System.out.println("Customer: " + order.getCustomer().get("name"));
            System.out.println("Items: " + order.getItems().size());
            System.out.println("Total: $" + order.getTotals().get("total"));

            // {{ORDER_PROCESSING_LOGIC}}
            // Example: Save to database, notify kitchen, etc.

            // Simulate processing
            Thread.sleep(100);

            // Calculate estimated ready time (30 minutes from now)
            String readyTime = Instant.now()
                    .plus(30, ChronoUnit.MINUTES)
                    .toString();

            return new WebhookResponse(
                    true,
                    order.getOrderId(),
                    "POS-" + System.currentTimeMillis(),
                    "Order accepted successfully",
                    readyTime
            );

        } catch (Exception e) {
            System.err.println("❌ Order processing error: " + e.getMessage());

            return new WebhookResponse(
                    false,
                    order.getOrderId(),
                    null,
                    "Order rejected: " + e.getMessage(),
                    null
            );
        }
    }

    /**
     * Webhook endpoint to receive orders from Grubtech
     */
    @PostMapping("/orders")
    public ResponseEntity<WebhookResponse> receiveOrder(
            @RequestBody String body,
            @RequestHeader(value = "X-Grubtech-Signature", required = false) String signature
    ) {
        try {
            // Verify signature
            if (!verifySignature(body, signature)) {
                return ResponseEntity
                        .status(HttpStatus.UNAUTHORIZED)
                        .body(new WebhookResponse(
                                false,
                                null,
                                null,
                                "Invalid signature",
                                null
                        ));
            }

            // Parse order (using Jackson ObjectMapper in real implementation)
            // For simplicity, this example uses a simplified approach
            Order order = parseOrder(body);

            // Validate order data
            if (order.getOrderId() == null || order.getItems() == null || order.getItems().isEmpty()) {
                return ResponseEntity
                        .status(HttpStatus.BAD_REQUEST)
                        .body(new WebhookResponse(
                                false,
                                null,
                                null,
                                "Invalid order data",
                                null
                        ));
            }

            // Process order
            WebhookResponse response = processOrder(order);

            // Send response
            HttpStatus status = response.isAccepted() ? HttpStatus.OK : HttpStatus.BAD_REQUEST;
            return ResponseEntity.status(status).body(response);

        } catch (Exception e) {
            System.err.println("❌ Webhook error: " + e.getMessage());
            return ResponseEntity
                    .status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(new WebhookResponse(
                            false,
                            null,
                            null,
                            "Internal server error",
                            null
                    ));
        }
    }

    /**
     * Parse order from JSON (simplified - use ObjectMapper in production)
     */
    private Order parseOrder(String body) {
        // In production, use Jackson ObjectMapper:
        // return objectMapper.readValue(body, Order.class);
        return new Order(); // Placeholder
    }

    /**
     * Health check endpoint
     */
    @GetMapping("/health")
    public Map<String, String> healthCheck() {
        return Map.of("status", "ok");
    }

    // Data models
    public static class Order {
        private String orderId;
        private Map<String, String> customer;
        private Map<String, Object> delivery;
        private List<Map<String, Object>> items;
        private Map<String, Double> totals;
        private Map<String, String> payment;

        // Getters and setters
        public String getOrderId() { return orderId; }
        public void setOrderId(String orderId) { this.orderId = orderId; }
        public Map<String, String> getCustomer() { return customer; }
        public void setCustomer(Map<String, String> customer) { this.customer = customer; }
        public Map<String, Object> getDelivery() { return delivery; }
        public void setDelivery(Map<String, Object> delivery) { this.delivery = delivery; }
        public List<Map<String, Object>> getItems() { return items; }
        public void setItems(List<Map<String, Object>> items) { this.items = items; }
        public Map<String, Double> getTotals() { return totals; }
        public void setTotals(Map<String, Double> totals) { this.totals = totals; }
        public Map<String, String> getPayment() { return payment; }
        public void setPayment(Map<String, String> payment) { this.payment = payment; }
    }

    public static class WebhookResponse {
        private boolean accepted;
        private String orderId;
        private String partnerOrderId;
        private String message;
        private String estimatedReadyTime;

        public WebhookResponse(boolean accepted, String orderId, String partnerOrderId,
                               String message, String estimatedReadyTime) {
            this.accepted = accepted;
            this.orderId = orderId;
            this.partnerOrderId = partnerOrderId;
            this.message = message;
            this.estimatedReadyTime = estimatedReadyTime;
        }

        // Getters
        public boolean isAccepted() { return accepted; }
        public String getOrderId() { return orderId; }
        public String getPartnerOrderId() { return partnerOrderId; }
        public String getMessage() { return message; }
        public String getEstimatedReadyTime() { return estimatedReadyTime; }
    }

    public static void main(String[] args) {
        SpringApplication.run(GrubtechOrderWebhook.class, args);
        System.out.println("✅ Webhook server started");
        System.out.println("Webhook URL: http://localhost:8080/webhooks/grubtech/orders");
    }
}
