package com.ai.trackwalker.uavaccess.flighthub;

import com.ai.trackwalker.uavaccess.config.UavAccessProperties;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.net.URI;
import java.time.Duration;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;

@Component
public class FlightHubOpenApiClient {
    private final UavAccessProperties properties;
    private final SecretTokenProvider tokenProvider;
    private final RestTemplate restTemplate;

    public FlightHubOpenApiClient(
            UavAccessProperties properties,
            SecretTokenProvider tokenProvider,
            RestTemplateBuilder builder
    ) {
        this.properties = properties;
        this.tokenProvider = tokenProvider;
        this.restTemplate = builder
                .setConnectTimeout(Duration.ofSeconds(Math.max(1, properties.getFlighthub().getConnectTimeoutSeconds())))
                .setReadTimeout(Duration.ofSeconds(Math.max(1, properties.getFlighthub().getReadTimeoutSeconds())))
                .build();
    }

    public Map<String, Object> get(String path, Map<String, ?> query) {
        return exchange(HttpMethod.GET, path, query, null);
    }

    public Map<String, Object> post(String path, Map<String, ?> body) {
        return exchange(HttpMethod.POST, path, Map.of(), body == null ? Map.of() : body);
    }

    public Map<String, Object> put(String path, Map<String, ?> body) {
        return exchange(HttpMethod.PUT, path, Map.of(), body == null ? Map.of() : body);
    }

    private Map<String, Object> exchange(
            HttpMethod method,
            String path,
            Map<String, ?> query,
            Map<String, ?> body
    ) {
        assertConfigured();
        String requestId = UUID.randomUUID().toString();
        URI uri = uri(path, query);
        try {
            ResponseEntity<Map> response = restTemplate.exchange(
                    uri,
                    method,
                    new HttpEntity<>(body, headers(requestId)),
                    Map.class
            );
            Map<String, Object> envelope = response.getBody() == null
                    ? Map.of()
                    : new LinkedHashMap<>(response.getBody());
            assertBusinessSuccess(envelope, requestId);
            Object data = envelope.get("data");
            if (data instanceof Map<?, ?> map) {
                @SuppressWarnings("unchecked")
                Map<String, Object> result = (Map<String, Object>) map;
                return result;
            }
            if (data == null) return Map.of();
            return Map.of("value", data);
        } catch (HttpStatusCodeException exception) {
            String code = String.valueOf(exception.getStatusCode().value());
            String message = exception.getResponseBodyAsString();
            try {
                Map<String, Object> error = com.ai.trackwalker.uavaccess.common.Jsons.map(message);
                code = String.valueOf(error.getOrDefault("code", code));
                message = String.valueOf(error.getOrDefault("message", message));
            } catch (RuntimeException ignored) {
                // Keep raw response body for the protocol audit classification.
            }
            throw failure(exception.getStatusCode().value(), code, requestId, message, exception);
        } catch (ResourceAccessException exception) {
            throw new FlightHubException(
                    503, "", "VENDOR_UNAVAILABLE", requestId,
                    "FlightHub request failed: " + exception.getMessage(), exception
            );
        }
    }

    private URI uri(String path, Map<String, ?> query) {
        String baseUrl = properties.getFlighthub().getBaseUrl().replaceAll("/+$", "");
        UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(baseUrl + path);
        MultiValueMap<String, String> values = new LinkedMultiValueMap<>();
        query.forEach((key, value) -> {
            if (value instanceof Iterable<?> iterable) {
                for (Object item : iterable) {
                    if (item != null) values.add(key, String.valueOf(item));
                }
            } else if (value != null) {
                values.add(key, String.valueOf(value));
            }
        });
        return builder.queryParams(values).build(true).toUri();
    }

    private HttpHeaders headers(String requestId) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.setAccept(java.util.List.of(MediaType.APPLICATION_JSON));
        headers.set("x-user-token", tokenProvider.token());
        headers.set("X-Project-Uuid", properties.getFlighthub().getProjectUuid());
        headers.set("X-Request-Id", requestId);
        return headers;
    }

    private void assertBusinessSuccess(Map<String, Object> envelope, String requestId) {
        Object rawCode = envelope.get("code");
        String code = rawCode == null ? "" : String.valueOf(rawCode);
        if ("0".equals(code) || "0.0".equals(code)) return;
        String message = String.valueOf(envelope.getOrDefault("message", "FlightHub business request failed"));
        throw failure(502, code, requestId, message, null);
    }

    private FlightHubException failure(
            int httpStatus,
            String vendorCode,
            String requestId,
            String message,
            Throwable cause
    ) {
        String classification = classify(httpStatus, vendorCode);
        int outwardStatus = switch (classification) {
            case "AUTHENTICATION_FAILED" -> 502;
            case "PERMISSION_DENIED" -> 502;
            case "INVALID_REQUEST", "SAFETY_PRECHECK_FAILED", "DEVICE_OFFLINE", "CONFLICT" -> 409;
            default -> 503;
        };
        return new FlightHubException(outwardStatus, vendorCode, classification, requestId, message, cause);
    }

    private String classify(int httpStatus, String code) {
        if (httpStatus == 401) return "AUTHENTICATION_FAILED";
        if (httpStatus == 403) return "PERMISSION_DENIED";
        if (httpStatus == 429) return "RATE_LIMITED";
        if (httpStatus >= 500) return "VENDOR_UNAVAILABLE";
        if (code.startsWith("212015")) return "DEVICE_OFFLINE";
        if (code.startsWith("219") || code.startsWith("514")) return "SAFETY_PRECHECK_FAILED";
        if (code.startsWith("200") || code.startsWith("210")) return "INVALID_REQUEST";
        if (httpStatus == 409) return "CONFLICT";
        return "UNKNOWN_VENDOR_ERROR";
    }

    private void assertConfigured() {
        UavAccessProperties.FlightHub config = properties.getFlighthub();
        if (!config.isEnabled()) {
            throw new FlightHubException(
                    503, "", "CONFIGURATION_REQUIRED", "",
                    "FlightHub integration is disabled", null
            );
        }
        java.util.List<String> missing = config.missingRequiredConfiguration(false);
        if (!missing.isEmpty()) {
            throw new FlightHubException(
                    503, "", "CONFIGURATION_REQUIRED", "",
                    "Missing FlightHub configuration: " + String.join(", ", missing), null
            );
        }
        URI base = URI.create(config.getBaseUrl());
        if (!"https".equalsIgnoreCase(base.getScheme()) && !config.isAllowInsecureHttp()) {
            throw new FlightHubException(
                    503, "", "CONFIGURATION_REQUIRED", "",
                    "FLIGHTHUB_BASE_URL must use HTTPS unless FLIGHTHUB_ALLOW_INSECURE_HTTP=true", null
            );
        }
        if (base.getUserInfo() != null) {
            throw new FlightHubException(
                    503, "", "CONFIGURATION_REQUIRED", "",
                    "FLIGHTHUB_BASE_URL must not contain user information", null
            );
        }
    }
}
