package com.goldenchart.upbit;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClientResponseException;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;

/**
 * 업비트 주문·계좌 REST API (실거래).
 */
@Component
@RequiredArgsConstructor
@Slf4j
public class UpbitOrderApiClient {

    private final WebClient.Builder webClientBuilder;
    private final ObjectMapper objectMapper;

    @Value("${upbit.api.base-url:https://api.upbit.com}")
    private String baseUrl;

    /**
     * 시장가 매수 (KRW 금액 지정).
     */
    public JsonNode placeMarketBuy(String accessKey, String secretKey,
                                   String market, double krwAmount) {
        Map<String, String> params = new TreeMap<>();
        params.put("market", market);
        params.put("side", "bid");
        params.put("price", String.valueOf((long) Math.floor(krwAmount)));
        params.put("ord_type", "price");
        return postOrder(accessKey, secretKey, params);
    }

    /**
     * 시장가 매도 (수량 지정).
     */
    public JsonNode placeMarketSell(String accessKey, String secretKey,
                                    String market, double volume) {
        Map<String, String> params = new TreeMap<>();
        params.put("market", market);
        params.put("side", "ask");
        params.put("volume", formatVolume(volume));
        params.put("ord_type", "market");
        return postOrder(accessKey, secretKey, params);
    }

    /** 보유 KRW 잔고 */
    public double getKrwBalance(String accessKey, String secretKey) {
        JsonNode accounts = getAccounts(accessKey, secretKey);
        if (accounts == null || !accounts.isArray()) return 0;
        for (JsonNode a : accounts) {
            if ("KRW".equals(a.path("currency").asText())) {
                return a.path("balance").asDouble(0);
            }
        }
        return 0;
    }

    /** 종목 보유 수량 */
    public double getCoinBalance(String accessKey, String secretKey, String market) {
        String currency = market.startsWith("KRW-") ? market.substring(4) : market;
        JsonNode accounts = getAccounts(accessKey, secretKey);
        if (accounts == null || !accounts.isArray()) return 0;
        for (JsonNode a : accounts) {
            if (currency.equals(a.path("currency").asText())) {
                return a.path("balance").asDouble(0);
            }
        }
        return 0;
    }

    public JsonNode getAccounts(String accessKey, String secretKey) {
        String token = UpbitJwtSigner.signNoQuery(accessKey, secretKey);
        try {
            String body = webClientBuilder.build()
                    .get()
                    .uri(baseUrl + "/v1/accounts")
                    .header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
                    .retrieve()
                    .bodyToMono(String.class)
                    .block();
            return objectMapper.readTree(body);
        } catch (WebClientResponseException e) {
            log.warn("[UpbitAPI] accounts {}: {}", e.getStatusCode(), e.getResponseBodyAsString());
            throw new UpbitApiException(e.getStatusCode().value(), e.getResponseBodyAsString());
        } catch (Exception e) {
            throw new UpbitApiException(0, e.getMessage());
        }
    }

    private JsonNode postOrder(String accessKey, String secretKey, Map<String, String> params) {
        String token = UpbitJwtSigner.sign(accessKey, secretKey, params);
        Map<String, Object> body = new LinkedHashMap<>(params);
        try {
            String response = webClientBuilder.build()
                    .post()
                    .uri(baseUrl + "/v1/orders")
                    .header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
                    .contentType(MediaType.APPLICATION_JSON)
                    .bodyValue(body)
                    .retrieve()
                    .bodyToMono(String.class)
                    .block();
            return objectMapper.readTree(response);
        } catch (WebClientResponseException e) {
            log.warn("[UpbitAPI] order {}: {}", e.getStatusCode(), e.getResponseBodyAsString());
            throw new UpbitApiException(e.getStatusCode().value(), e.getResponseBodyAsString());
        } catch (Exception e) {
            throw new UpbitApiException(0, e.getMessage());
        }
    }

    private static String formatVolume(double v) {
        if (v >= 1) return String.format("%.8f", v).replaceAll("0+$", "").replaceAll("\\.$", "");
        return String.format("%.8f", v);
    }

    public static class UpbitApiException extends RuntimeException {
        public final int status;
        public UpbitApiException(int status, String message) {
            super("Upbit API " + status + ": " + message);
            this.status = status;
        }
    }
}
