/**
 * @description Grubtech API Authentication - Java (Spring Boot)
 * @author Grubtech Integration Team
 * @version 1.0.0
 *
 * This example demonstrates how to authenticate with the Grubtech API
 * using an API key to obtain an access token.
 *
 * Prerequisites:
 * - Java 17+
 * - Spring Boot 3.x
 * - Spring WebFlux (for WebClient)
 *
 * Dependencies (Maven):
 * <dependency>
 *   <groupId>org.springframework.boot</groupId>
 *   <artifactId>spring-boot-starter-webflux</artifactId>
 * </dependency>
 *
 * Replace the following placeholders:
 * - {{API_KEY}}: Your Grubtech API key
 * - {{PARTNER_ID}}: Your partner identifier
 */

package com.example.grubtech;

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

import java.util.HashMap;
import java.util.Map;

public class GrubtechAuthentication {

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

    private final WebClient webClient;

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

    /**
     * Authenticate with Grubtech API and obtain access token
     *
     * @return Access token for API requests
     * @throws RuntimeException if authentication fails
     */
    public String authenticate() {
        try {
            // Construct authentication request body
            Map<String, String> requestBody = new HashMap<>();
            requestBody.put("partnerId", PARTNER_ID);
            requestBody.put("grantType", "api_key");

            // Make authentication request
            AuthResponse response = webClient.post()
                    .uri("/v1/auth/token")
                    .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                    .header("x-api-key", API_KEY)
                    .bodyValue(requestBody)
                    .retrieve()
                    .onStatus(
                            status -> !status.is2xxSuccessful(),
                            clientResponse -> clientResponse.bodyToMono(String.class)
                                    .flatMap(errorBody -> Mono.error(
                                            new RuntimeException(
                                                    "Authentication failed: " +
                                                    clientResponse.statusCode() +
                                                    " - " + errorBody
                                            )
                                    ))
                    )
                    .bodyToMono(AuthResponse.class)
                    .block();

            if (response == null || response.getToken() == null) {
                throw new RuntimeException("No token received from authentication");
            }

            System.out.println("Authentication successful!");
            System.out.println("Token expires in: " + response.getExpiresIn() + " seconds");

            return response.getToken();

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

    /**
     * Example: Use token in subsequent API requests
     *
     * @param token Access token from authenticate()
     * @return API response
     */
    public Map<String, Object> exampleApiCall(String token) {
        return webClient.get()
                .uri("/v1/menus")
                .header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .retrieve()
                .onStatus(
                        status -> !status.is2xxSuccessful(),
                        clientResponse -> Mono.error(
                                new RuntimeException("API call failed: " + clientResponse.statusCode())
                        )
                )
                .bodyToMono(Map.class)
                .block();
    }

    /**
     * Response model for authentication endpoint
     */
    public static class AuthResponse {
        private String token;
        private int expiresIn;
        private String tokenType;

        // Getters and setters
        public String getToken() { return token; }
        public void setToken(String token) { this.token = token; }

        public int getExpiresIn() { return expiresIn; }
        public void setExpiresIn(int expiresIn) { this.expiresIn = expiresIn; }

        public String getTokenType() { return tokenType; }
        public void setTokenType(String tokenType) { this.tokenType = tokenType; }
    }

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

            // Authenticate and get token
            String token = auth.authenticate();

            // Use token for API calls
            Map<String, Object> menus = auth.exampleApiCall(token);
            System.out.println("Menus: " + menus);

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