import time
import threading
import logging
import sys
import os
import pandas as pd
from datetime import datetime
from typing import List
from logging.handlers import RotatingFileHandler
from config import WaveStrategyConfig, DBConfig
from database import DatabaseManager
from wave_strategy import WaveRangeStrategy
from virtual_exchange import VirtualExchange
from param_store import ParameterStore

logger = logging.getLogger(__name__)

def setup_logging(log_file: str, level: str = 'INFO'):
    """Настройка логирования для службы (не конфликтует с stdout бэктеста)"""
    os.makedirs(os.path.dirname(log_file), exist_ok=True)
    
    file_handler = RotatingFileHandler(
        log_file, maxBytes=10*1024*1024, backupCount=5, encoding='utf-8'
    )
    file_handler.setFormatter(logging.Formatter('%(asctime)s | %(levelname)-8s | %(name)s | %(message)s'))

    console = logging.StreamHandler(sys.stdout)
    console.setFormatter(logging.Formatter('%(levelname)-8s | %(message)s'))

    root = logging.getLogger()
    root.handlers = []
    root.setLevel(getattr(logging, level.upper(), logging.INFO))
    root.addHandler(file_handler)
    root.addHandler(console)

class TradingBotService:
    def __init__(self, config: WaveStrategyConfig, db_config: DBConfig):
        setup_logging(config.LOG_FILE, config.LOG_LEVEL)
        self.config = config
        self.db = DatabaseManager(db_config)
        self.strategy = WaveRangeStrategy(config)
        self.exchange = VirtualExchange(config)
        self.running = False
        self._stop = threading.Event()
        self._last_trigger_time = {}
        logger.info("🤖 Запуск службы")

    def _check_schedule(self, timeframe: str) -> bool:
        """Проверка, наступило ли время анализа (+-1 мин от начала периода)"""
        now = datetime.now()
        cfg = self.config.TIMEFRAMES.get(timeframe)
        if not cfg:
            return False
            
        offset = cfg['offset_minutes']
        trigger_key = f"{timeframe}_{now.date()}"
        
        # Защита от повторного срабатывания
        if self._last_trigger_time.get(timeframe) == now.date():
            return False

        if timeframe == 'H1':
            if now.minute == offset and now.second < 30:
                self._last_trigger_time[timeframe] = now.date()
                return True
        elif timeframe == 'D1':
            if now.hour == 0 and now.minute == offset and now.second < 30:
                self._last_trigger_time[timeframe] = now.date()
                return True
        elif timeframe == 'W1':
            if now.weekday() == 0 and now.hour == 0 and now.minute == offset and now.second < 30:
                self._last_trigger_time[timeframe] = now.date()
                return True
        return False

    def _process_instrument(self, instrument: str, timeframe: str):
        try:
            # Увеличен лимит для корректного расчета длинных волн
            df = self.db.fetch_ohlc_data(instrument, timeframe, limit=max(500, self.config.wave_lookback + 200))
            if df.empty or len(df) < self.config.atr_period + 50:
                return

            current_close = float(df['Close'].iloc[-1])
            current_high = float(df['High'].iloc[-1])
            current_low = float(df['Low'].iloc[-1])

            atr_series = self.exchange.strategy.calculate_atr(df, self.config.atr_period)
            if atr_series.empty or pd.isna(atr_series.iloc[-1]):
                return
            current_atr = float(atr_series.iloc[-1])
            
            if current_atr <= 0 or current_close <= 0:
                return

            # 1. Анализ и генерация сигнала
            signal = self.exchange.strategy.analyze(df, instrument, timeframe, debug=False)
            if signal:
                logger.info(f"🎯 СИГНАЛ: {signal.signal.name} {instrument}_{timeframe} @ {signal.price:.4f} | "
                            f"SL: {signal.stop_loss:.4f} | TP: {signal.take_profit:.4f} | "
                            f"R:R: {signal.rr_ratio:.2f} | {signal.reason}")
                self.exchange.execute_signal(signal)

            # 2. Управление позициями (трейлинг, SL/TP, выходы)
            self.exchange.update_price(instrument, current_close, current_high, current_low, current_atr)

        except Exception as e:
            logger.error(f"❌ Ошибка {instrument}_{timeframe}: {e}", exc_info=True)

    def _loop(self):
        logger.info("🔄 Планировщик запущен")
        instruments = self.db.get_available_instruments()
        if not instruments:
            logger.warning("⚠️ Инструменты не найдены. Проверьте БД.")
            return
            
        while not self._stop.is_set():
            try:
                for tf in self.config.TIMEFRAMES:
                    if self._check_schedule(tf):
                        logger.info(f"⏰ Анализ {tf}: {len(instruments)} инструментов")
                        for inst in instruments:
                            if self._stop.is_set():
                                break
                            self._process_instrument(inst, tf)
                            time.sleep(0.05)
            except Exception as e:
                logger.error(f"❌ Ошибка в цикле планировщика: {e}", exc_info=True)
                
            time.sleep(30)

    def start(self):
        if self.running:
            return
        self.running = True
        self._stop.clear()
        threading.Thread(target=self._loop, daemon=True).start()
        logger.info("✅ Служба запущена (Ctrl+C для остановки)")

    def stop(self):
        logger.info("🛑 Остановка...")
        self.running = False
        self._stop.set()
        logger.info("✅ Остановлено")

    def run_blocking(self):
        self.start()
        try:
            while self.running:
                time.sleep(60)
        except KeyboardInterrupt:
            pass
        finally:
            self.stop()