package com.cloudhandson.vpdbackoffice.service;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.net.URI;
import java.util.UUID;
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.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

/** Calls the ORDS boundary; the database endpoint owns bearer-to-row-access-context mapping. */
@Service
public class SelectAiAgentOrdsService {

  static final String ORDS_PATH = "/cb-ords/kb-select-ai-vpd/query";
  private static final int MAX_PROMPT_LENGTH = 4_000;
  private static final int MAX_LIMIT = 100;

  private final SettingService settingService;
  private final RestTemplate restTemplate;
  private final ObjectMapper objectMapper;

  public SelectAiAgentOrdsService(
      SettingService settingService,
      RestTemplate ordsAgentRestTemplate,
      ObjectMapper objectMapper
  ) {
    this.settingService = settingService;
    this.restTemplate = ordsAgentRestTemplate;
    this.objectMapper = objectMapper;
  }

  /**
   * Compatibility overload for callers of the former SQL-generation tool.
   * The row-access query endpoint is stateless and therefore ignores conversationId.
   */
  public JsonNode run(String bearerToken, String prompt, String conversationId) {
    return run(bearerToken, prompt, 50);
  }

  public JsonNode run(String bearerToken, String prompt, int limit) {
    String normalizedToken = required(bearerToken, "bearerToken");
    String normalizedPrompt = required(prompt, "prompt");
    if (normalizedPrompt.length() > MAX_PROMPT_LENGTH) {
      throw new AppException("prompt는 " + MAX_PROMPT_LENGTH + "자 이하여야 합니다.");
    }
    int normalizedLimit = normalizeLimit(limit);
    String baseUrl = settingService.ordsBaseUrl();
    if (baseUrl == null || baseUrl.isBlank()) {
      throw new AppException("ORDS base URL이 설정되지 않았습니다.");
    }

    ObjectNode requestBody = objectMapper.createObjectNode();
    requestBody.put("prompt", normalizedPrompt);
    requestBody.put("limit", normalizedLimit);

    HttpHeaders headers = new HttpHeaders();
    headers.setBearerAuth(normalizedToken);
    headers.setContentType(MediaType.APPLICATION_JSON);
    headers.set("X-VPD-Probe-Id", UUID.randomUUID().toString());

    try {
      ResponseEntity<String> response = restTemplate.exchange(
          endpoint(baseUrl),
          HttpMethod.POST,
          new HttpEntity<>(requestBody.toString(), headers),
          String.class
      );
      JsonNode body = parse(response.getBody());
      if (body.hasNonNull("error")) {
        throw new AppException("Select AI 행 접근 ORDS 오류: " + body.path("error").asText());
      }
      return body;
    } catch (HttpStatusCodeException e) {
      if (e.getStatusCode().isSameCodeAs(HttpStatus.UNAUTHORIZED)
          || e.getStatusCode().isSameCodeAs(HttpStatus.FORBIDDEN)) {
        throw new VpdTokenAccessDeniedException();
      }
      throw new AppException("Select AI 행 접근 ORDS HTTP " + e.getStatusCode().value()
          + ": " + responseError(e.getResponseBodyAsString()));
    } catch (ResourceAccessException e) {
      throw new AppException("Select AI 행 접근 ORDS 연결 또는 응답 시간 초과: " + e.getMessage());
    }
  }

  private URI endpoint(String baseUrl) {
    return UriComponentsBuilder.fromUriString(baseUrl)
        .path(ORDS_PATH)
        .build()
        .toUri();
  }

  private JsonNode parse(String value) {
    try {
      if (value == null || value.isBlank()) {
        throw new AppException("Select AI 행 접근 ORDS 응답 본문이 비어 있습니다.");
      }
      return objectMapper.readTree(value);
    } catch (AppException e) {
      throw e;
    } catch (Exception e) {
      throw new AppException("Select AI 행 접근 ORDS 응답 JSON 파싱 실패: " + e.getMessage());
    }
  }

  private String responseError(String body) {
    try {
      JsonNode parsed = objectMapper.readTree(body);
      return parsed.path("error").asText(body == null ? "" : body);
    } catch (Exception ignored) {
      return body == null ? "" : body;
    }
  }

  private String required(String value, String name) {
    if (value == null || value.isBlank()) {
      throw new AppException(name + "은(는) 필수입니다.");
    }
    return value.trim();
  }

  private int normalizeLimit(int value) {
    if (value < 1) {
      return 50;
    }
    return Math.min(value, MAX_LIMIT);
  }
}
