package com.goldenchart.trading.pipeline;

import com.goldenchart.dto.BacktestSettingsDto;
import com.goldenchart.entity.GcAppSettings;
import com.goldenchart.entity.GcPaperAccount;
import com.goldenchart.entity.GcPaperPosition;
import com.goldenchart.repository.GcPaperAccountRepository;
import com.goldenchart.repository.GcPaperPositionRepository;
import com.goldenchart.service.AppSettingsService;
import com.goldenchart.service.BacktestSettingsService;
import com.goldenchart.trading.TradingExecutionService;
import com.goldenchart.trading.TradingMode;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

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

/**
 * 봉 마감을 기다리지 않고 틱 단위로 손절/익절을 검사한다.
 * 백테스트 설정의 SL/TP 비율을 모의투자 실시간 감시에 재사용한다.
 */
@Component
@RequiredArgsConstructor
@Slf4j
public class RealtimePositionMonitor {

    private final GcPaperPositionRepository positionRepo;
    private final GcPaperAccountRepository accountRepo;
    private final BacktestSettingsService backtestSettingsService;
    private final AppSettingsService appSettingsService;
    private final TradingExecutionService tradingExecutionService;
    private final TradingPipelineProperties props;

    /** 동일 종목·계정에 중복 청산 주문 방지 (ms) */
    private final Map<String, Long> lastExitMs = new ConcurrentHashMap<>();
    private static final long EXIT_COOLDOWN_MS = 3_000;

    public void onTick(String market, double tradePrice) {
        if (!props.isEnabled() || tradePrice <= 0) return;

        List<GcPaperPosition> positions =
                positionRepo.findBySymbolAndQuantityGreaterThan(market, BigDecimal.ZERO);
        if (positions.isEmpty()) return;

        for (GcPaperPosition pos : positions) {
            double qty = pos.getQuantity().doubleValue();
            if (qty <= 0) continue;

            GcPaperAccount account = accountRepo.findById(pos.getAccountId()).orElse(null);
            if (account == null) continue;

            String deviceId = account.getDeviceId();
            Long userId = account.getUserId();
            if (deviceId == null || deviceId.isBlank()) continue;

            GcAppSettings app;
            try {
                app = appSettingsService.getEntity(userId, deviceId);
            } catch (Exception e) {
                continue;
            }
            TradingMode mode = TradingMode.fromString(app.getTradingMode());
            boolean paperPos = mode.usePaper() && Boolean.TRUE.equals(app.getPaperTradingEnabled());
            if (!paperPos && !mode.useLive()) continue;

            String cooldownKey = account.getId() + ":" + market;
            long now = System.currentTimeMillis();
            Long last = lastExitMs.get(cooldownKey);
            if (last != null && now - last < EXIT_COOLDOWN_MS) continue;

            double avg = pos.getAvgPrice().doubleValue();
            if (avg <= 0) continue;

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

            if (Boolean.TRUE.equals(risk.getStopLossEnabled())
                    && risk.getStopLossPct() != null
                    && pnlPct <= -risk.getStopLossPct().doubleValue()) {
                lastExitMs.put(cooldownKey, now);
                double sl = risk.getStopLossPct().doubleValue();
                tradingExecutionService.executeRiskExit(
                        OrderRequest.stopLoss(deviceId, userId, market, tradePrice, sl));
                log.info("[SL] {} @ {} pnl={}% (limit -{}%)", market, tradePrice,
                        String.format("%.2f", pnlPct), sl);
                return;
            }

            if (Boolean.TRUE.equals(risk.getTakeProfitEnabled())
                    && risk.getTakeProfitPct() != null
                    && pnlPct >= risk.getTakeProfitPct().doubleValue()) {
                lastExitMs.put(cooldownKey, now);
                double tp = risk.getTakeProfitPct().doubleValue();
                tradingExecutionService.executeRiskExit(
                        OrderRequest.takeProfit(deviceId, userId, market, tradePrice, tp));
                log.info("[TP] {} @ {} pnl={}% (target +{}%)", market, tradePrice,
                        String.format("%.2f", pnlPct), tp);
                return;
            }
        }
    }
}
