/**
 * Grubtech Item Availability Update - Java (Spring Boot)
 *
 * This example demonstrates how to update menu item availability in Grubtech
 * to keep items in sync with your inventory status.
 *
 * Prerequisites:
 * - Java 17+
 * - Spring Boot 3.x
 * - Spring WebFlux (for WebClient)
 *
 * Replace the following placeholders:
 * - {{AUTH_TOKEN}}: Access token from authentication step
 * - {{ITEM_ID}}: The menu item ID to update
 * - {{AVAILABLE_STATUS}}: Boolean availability status (true/false)
 */

package com.example.grubtech;

import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import java.time.Instant;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class GrubtechItemAvailabilityService {

    private static final String AUTH_TOKEN = "{{AUTH_TOKEN}}";
    private static final String BASE_URL = "https://api.staging.grubtech.io";

    private final WebClient webClient;

    public GrubtechItemAvailabilityService() {
        this.webClient = WebClient.builder()
                .baseUrl(BASE_URL)
                .build();
    }

    /**
     * Update single item availability
     *
     * @param itemId    The menu item ID
     * @param available True if in stock, False if out of stock
     * @param reason    Optional reason for status change
     * @return Availability update response
     */
    public AvailabilityResponse updateItemAvailability(
            String itemId,
            boolean available,
            String reason
    ) {
        try {
            // Construct payload
            Map<String, Object> payload = new HashMap<>();
            payload.put("itemId", itemId);
            payload.put("available", available);
            payload.put("timestamp", Instant.now().toString());

            if (reason != null && !reason.isEmpty()) {
                payload.put("reason", reason);
            }

            System.out.println("Updating item " + itemId + ": available=" + available);

            // Make availability update request
            AvailabilityResponse response = webClient.put()
                    .uri("/v1/items/" + itemId + "/availability")
                    .header(HttpHeaders.AUTHORIZATION, "Bearer " + AUTH_TOKEN)
                    .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                    .bodyValue(payload)
                    .retrieve()
                    .onStatus(
                            status -> !status.is2xxSuccessful(),
                            clientResponse -> clientResponse.bodyToMono(String.class)
                                    .flatMap(errorBody -> Mono.error(
                                            new RuntimeException(
                                                    "Availability update failed: " +
                                                    clientResponse.statusCode() +
                                                    " - " + errorBody
                                            )
                                    ))
                    )
                    .bodyToMono(AvailabilityResponse.class)
                    .block();

            if (response == null) {
                throw new RuntimeException("No response received");
            }

            System.out.println("✅ Item availability updated successfully!");
            System.out.println("Item ID: " + response.getItemId());
            System.out.println("Available: " + available);

            return response;

        } catch (Exception e) {
            System.err.println("❌ Availability update error: " + e.getMessage());
            throw e;
        }
    }

    /**
     * Update multiple items availability in bulk
     *
     * @param updates List of item availability updates
     * @return Bulk availability update response
     */
    public BulkAvailabilityResponse updateBulkAvailability(
            List<ItemUpdate> updates
    ) {
        try {
            // Construct bulk payload
            Map<String, Object> payload = new HashMap<>();
            payload.put("updates", updates);
            payload.put("timestamp", Instant.now().toString());

            System.out.println("Updating " + updates.size() + " items availability");

            // Make bulk availability update request
            BulkAvailabilityResponse response = webClient.put()
                    .uri("/v1/items/availability/bulk")
                    .header(HttpHeaders.AUTHORIZATION, "Bearer " + AUTH_TOKEN)
                    .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                    .bodyValue(payload)
                    .retrieve()
                    .onStatus(
                            status -> !status.is2xxSuccessful(),
                            clientResponse -> Mono.error(
                                    new RuntimeException(
                                            "Bulk availability update failed: " +
                                            clientResponse.statusCode()
                                    )
                            )
                    )
                    .bodyToMono(BulkAvailabilityResponse.class)
                    .block();

            if (response == null) {
                throw new RuntimeException("No response received");
            }

            System.out.println("✅ Bulk availability updated successfully!");
            System.out.println("Updated: " + response.getUpdatedCount() + " items");

            // Report any errors
            if (response.getErrors() != null && !response.getErrors().isEmpty()) {
                System.out.println("⚠️  " + response.getErrors().size() + " items had errors:");
                response.getErrors().forEach(err ->
                        System.out.println("  - " + err.getItemId() + ": " + err.getError())
                );
            }

            return response;

        } catch (Exception e) {
            System.err.println("❌ Bulk availability update error: " + e.getMessage());
            throw e;
        }
    }

    /**
     * Mark item as out of stock
     */
    public AvailabilityResponse markOutOfStock(String itemId) {
        return updateItemAvailability(itemId, false, "Out of stock");
    }

    /**
     * Mark item as back in stock
     */
    public AvailabilityResponse markInStock(String itemId) {
        return updateItemAvailability(itemId, true, "Back in stock");
    }

    /**
     * Sync inventory with Grubtech
     */
    public BulkAvailabilityResponse syncInventory(Map<String, Boolean> inventory) {
        List<ItemUpdate> updates = inventory.entrySet().stream()
                .map(entry -> new ItemUpdate(
                        entry.getKey(),
                        entry.getValue(),
                        entry.getValue() ? "In stock" : "Out of stock"
                ))
                .collect(Collectors.toList());

        return updateBulkAvailability(updates);
    }

    // Data models
    public static class ItemUpdate {
        private String itemId;
        private boolean available;
        private String reason;

        public ItemUpdate(String itemId, boolean available, String reason) {
            this.itemId = itemId;
            this.available = available;
            this.reason = reason;
        }

        // Getters
        public String getItemId() { return itemId; }
        public boolean isAvailable() { return available; }
        public String getReason() { return reason; }
    }

    public static class AvailabilityResponse {
        private boolean success;
        private String itemId;
        private String message;

        // Getters and setters
        public boolean isSuccess() { return success; }
        public void setSuccess(boolean success) { this.success = success; }
        public String getItemId() { return itemId; }
        public void setItemId(String itemId) { this.itemId = itemId; }
        public String getMessage() { return message; }
        public void setMessage(String message) { this.message = message; }
    }

    public static class BulkAvailabilityResponse {
        private boolean success;
        private int updatedCount;
        private List<ItemError> errors;

        // Getters and setters
        public boolean isSuccess() { return success; }
        public void setSuccess(boolean success) { this.success = success; }
        public int getUpdatedCount() { return updatedCount; }
        public void setUpdatedCount(int updatedCount) { this.updatedCount = updatedCount; }
        public List<ItemError> getErrors() { return errors; }
        public void setErrors(List<ItemError> errors) { this.errors = errors; }
    }

    public static class ItemError {
        private String itemId;
        private String error;

        // Getters and setters
        public String getItemId() { return itemId; }
        public void setItemId(String itemId) { this.itemId = itemId; }
        public String getError() { return error; }
        public void setError(String error) { this.error = error; }
    }

    // Main method for testing
    public static void main(String[] args) {
        try {
            GrubtechItemAvailabilityService service = new GrubtechItemAvailabilityService();

            String itemId = "{{ITEM_ID}}";
            boolean available = {{AVAILABLE_STATUS}}; // true or false

            // Example 1: Single item update
            service.updateItemAvailability(itemId, available, "Inventory sync");

            // Example 2: Mark item out of stock
            // service.markOutOfStock("item-123");

            // Example 3: Bulk update from inventory
            // service.syncInventory(Map.of(
            //     "item-123", false,  // Out of stock
            //     "item-456", true,   // In stock
            //     "item-789", true    // In stock
            // ));

        } catch (Exception e) {
            System.exit(1);
        }
    }
}
