package com.goldenchart.websocket;

import com.goldenchart.dto.CandleBarDto;
import com.goldenchart.entity.GcLiveStrategySettings;
import com.goldenchart.repository.GcLiveStrategySettingsRepository;
import com.goldenchart.service.LiveStrategyEvaluator;
import com.goldenchart.service.StrategyConditionTimeframeService;
import com.goldenchart.service.TradeSignalService;
import com.goldenchart.trading.pipeline.OrderExecutionQueue;
import com.goldenchart.storage.Ta4jStorage;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.ta4j.core.BarSeries;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * 3초 주기 라운드 로빈 스케줄러.
 *
 * <p>설정 방식이 REALTIME_TICK 이고 isLiveCheck = true 인 종목에 대해
 * 현재 진행 중인 캔들 인덱스(series.getEndIndex())로 전략을 평가하여
 * BUY/SELL 시그널이 발생하면 STOMP 토픽으로 발행한다.
 *
 * <p>동시성 안전:
 * <ul>
 *   <li>BarBuilder 의 partialBars 와 Ta4jStorage 는 ConcurrentHashMap 기반</li>
 *   <li>이 스케줄러는 단일 스레드로 돌기 때문에 중복 평가 없음</li>
 * </ul>
 */
@Component
@RequiredArgsConstructor
@Slf4j
public class LiveStrategyScheduler {

    private final GcLiveStrategySettingsRepository settingsRepo;
    private final LiveStrategyEvaluator            evaluator;
    private final Ta4jStorage                      ta4jStorage;
    private final TradingWebSocketBroker           broker;
    private final TradeSignalService               tradeSignalService;
    private final OrderExecutionQueue              orderExecutionQueue;
    private final StrategyConditionTimeframeService conditionTimeframes;

    /**
     * 3초마다 REALTIME_TICK 설정 종목을 순회하여 전략 판정 후 시그널 발행.
     * fixedDelay=3000: 이전 실행이 끝난 뒤 3초 후 재실행 (오버랩 방지).
     */
    @Scheduled(fixedDelay = 3000)
    public void tick() {
        List<GcLiveStrategySettings> activeSettings = settingsRepo.findAllByIsLiveCheckTrue();
        if (activeSettings.isEmpty()) return;

        for (GcLiveStrategySettings s : activeSettings) {
            if (!"REALTIME_TICK".equals(s.getExecutionType())) continue;
            if (s.getStrategyId() == null) continue;

            String market = s.getMarket();
            for (String candleType : conditionTimeframes.collectForStrategy(s.getStrategyId())) {
                evaluateRealtimeTickForMarket(s, market, candleType);
            }
        }
    }

    private void evaluateRealtimeTickForMarket(GcLiveStrategySettings s, String market,
                                                String candleType) {
            if (!ta4jStorage.exists(market, candleType)) return;

            BarSeries series = ta4jStorage.getOrCreate(market, candleType);
            if (series.isEmpty()) return;

            String signal = evaluator.evaluateRealtimeTick(market, candleType);
            if ("BUY".equals(signal) || "SELL".equals(signal)) {
                // 현재 진행 중인 캔들 정보 조회
                org.ta4j.core.Bar lastBar = series.getLastBar();
                // Ta4j 0.22: getEndTime() → Instant; 시작 시간 = endTime - duration
                long barStartEpoch = lastBar.getEndTime().getEpochSecond()
                        - lastBar.getTimePeriod().getSeconds();

                CandleBarDto dto = CandleBarDto.builder()
                        .time(barStartEpoch)
                        .open(lastBar.getOpenPrice().doubleValue())
                        .high(lastBar.getHighPrice().doubleValue())
                        .low(lastBar.getLowPrice().doubleValue())
                        .close(lastBar.getClosePrice().doubleValue())
                        .volume(lastBar.getVolume().doubleValue())
                        .signal(signal)
                        .build();

                broker.publish(market, candleType, dto);
                log.info("[LiveStrategyScheduler] REALTIME_TICK signal={} market={}", signal, market);

                // DB 저장
                try {
                    tradeSignalService.save(
                            s.getDeviceId(), s.getUserId(),
                            market, s.getStrategyId(), null,
                            signal,
                            lastBar.getClosePrice().doubleValue(),
                            barStartEpoch, candleType, "REALTIME_TICK"
                    );
                    if (s.getDeviceId() != null) {
                        orderExecutionQueue.submitSignal(
                                s.getDeviceId(), s.getUserId(), market,
                                s.getStrategyId(), signal,
                                lastBar.getClosePrice().doubleValue());
                    }
                } catch (Exception ex) {
                    log.warn("[LiveStrategyScheduler] 시그널 DB 저장 실패: {}", ex.getMessage());
                }
            }
    }
}
