package com.goldenchart.service;

import com.fasterxml.jackson.databind.JsonNode;
import com.goldenchart.dto.BacktestSettingsDto;
import com.goldenchart.entity.GcAppSettings;
import com.goldenchart.repository.GcAppSettingsRepository;
import com.goldenchart.trading.TradingExecutionService;
import com.goldenchart.trading.TradingMode;
import com.goldenchart.trading.pipeline.OrderRequest;
import com.goldenchart.upbit.UpbitOrderApiClient;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 실거래 포지션 틱 단위 손절/익절 — 계좌 스냅샷(30초) + 틱 비교.
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class LiveRiskMonitorService {

    private final GcAppSettingsRepository appSettingsRepo;
    private final AppSettingsService appSettingsService;
    private final BacktestSettingsService backtestSettingsService;
    private final UpbitOrderApiClient upbitApi;
    private final LiveTradingService liveTradingService;
    private final TradingExecutionService tradingExecutionService;

    private final Map<String, Long> exitCooldown = new ConcurrentHashMap<>();
    /** deviceId → currency → {balance, avgPrice} */
    private final Map<String, Map<String, double[]>> accountCache = new ConcurrentHashMap<>();

    private static final long COOLDOWN_MS = 5_000;

    @PostConstruct
    void init() {
        refreshAccounts();
    }

    @Scheduled(fixedDelay = 30_000)
    public void refreshAccounts() {
        for (GcAppSettings app : appSettingsRepo.findAll()) {
            if (!Boolean.TRUE.equals(app.getLiveAutoTradeEnabled())) continue;
            if (!liveTradingService.hasApiKeys(app)) continue;
            if (!TradingMode.fromString(app.getTradingMode()).useLive()) continue;
            String deviceId = app.getDeviceId();
            if (deviceId == null || deviceId.isBlank()) continue;
            try {
                var creds = appSettingsService.resolveUpbitCredentials(app);
                if (!creds.isComplete()) continue;
                JsonNode accounts = upbitApi.getAccounts(
                        creds.accessKey(), creds.secretKey());
                Map<String, double[]> coins = new ConcurrentHashMap<>();
                if (accounts != null && accounts.isArray()) {
                    for (JsonNode a : accounts) {
                        String cur = a.path("currency").asText();
                        double bal = a.path("balance").asDouble(0);
                        if (bal <= 0 || "KRW".equals(cur)) continue;
                        double avg = a.path("avg_buy_price").asDouble(0);
                        coins.put(cur, new double[]{bal, avg});
                    }
                }
                accountCache.put(deviceId, coins);
            } catch (Exception e) {
                log.debug("[LiveRisk] account sync {}: {}", deviceId, e.getMessage());
            }
        }
    }

    public void onTick(String market, double tradePrice) {
        if (tradePrice <= 0) return;
        String currency = market.startsWith("KRW-") ? market.substring(4) : market;

        for (var entry : accountCache.entrySet()) {
            String deviceId = entry.getKey();
            double[] pos = entry.getValue().get(currency);
            if (pos == null || pos[0] <= 0 || pos[1] <= 0) continue;

            String key = deviceId + ":" + market;
            long now = System.currentTimeMillis();
            Long last = exitCooldown.get(key);
            if (last != null && now - last < COOLDOWN_MS) continue;

            GcAppSettings app = appSettingsRepo.findByDeviceId(deviceId).orElse(null);
            if (app == null) continue;

            BacktestSettingsDto risk = backtestSettingsService.get(deviceId);
            double avg = pos[1];
            double pnlPct = (tradePrice - avg) / avg * 100.0;

            if (Boolean.TRUE.equals(risk.getStopLossEnabled())
                    && risk.getStopLossPct() != null
                    && pnlPct <= -risk.getStopLossPct().doubleValue()) {
                exitCooldown.put(key, now);
                tradingExecutionService.executeRiskExit(
                        OrderRequest.stopLoss(deviceId, app.getUserId(), market, tradePrice,
                                risk.getStopLossPct().doubleValue()));
                return;
            }
            if (Boolean.TRUE.equals(risk.getTakeProfitEnabled())
                    && risk.getTakeProfitPct() != null
                    && pnlPct >= risk.getTakeProfitPct().doubleValue()) {
                exitCooldown.put(key, now);
                tradingExecutionService.executeRiskExit(
                        OrderRequest.takeProfit(deviceId, app.getUserId(), market, tradePrice,
                                risk.getTakeProfitPct().doubleValue()));
                return;
            }
        }
    }
}
