import logging
from typing import Optional
from sqlalchemy.ext.asyncio import AsyncSession

from app.config import settings
from app.data_layer.market_data import fetch_multi_timeframe, get_current_price
from app.engines.decision_engine import DecisionEngine
from app.engines.risk_engine import risk_engine
from app.engines.execution_engine import execution_engine
from app.engines.simulation_engine import simulation_engine
from app.models.trade import Trade, TradeStatus
from app.models.strategy import Strategy
from app.services.telegram_service import telegram_service
from sqlalchemy import select

logger = logging.getLogger(__name__)


class TradeService:
    """
    Orchestrates the full trade lifecycle:
    1. Fetch market data
    2. Run decision engine
    3. Apply risk management
    4. Execute or simulate the trade
    5. Send Telegram notification
    """

    def __init__(self, decision_engine: DecisionEngine):
        self.decision_engine = decision_engine

    async def run_signal_cycle(self, pair: str, db: AsyncSession) -> Optional[Trade]:
        """
        Run one full analysis and execution cycle for a trading pair.
        Returns the opened Trade if a signal is acted on, else None.
        """
        # Skip if bot is paused via Telegram
        if not telegram_service.is_running:
            logger.info("Bot is paused, skipping signal cycle")
            return None

        # Check max open trades
        result = await db.execute(
            select(Trade).where(Trade.status == TradeStatus.OPEN)
        )
        open_count = len(result.scalars().all())
        if not risk_engine.can_open_trade(open_count):
            logger.info(f"Max open trades reached ({open_count}), skipping {pair}")
            return None

        # Fetch strategy weights from DB
        strat_result = await db.execute(select(Strategy))
        strategies_db = strat_result.scalars().all()
        strategy_weights = {s.name: s.weight for s in strategies_db if s.enabled}

        # Fetch OHLCV data for multiple timeframes
        try:
            market_data = fetch_multi_timeframe(pair, timeframes=["15m", "1h"])
        except Exception as e:
            logger.error(f"Failed to fetch market data for {pair}: {e}")
            return None

        current_price = get_current_price(pair)
        if not current_price:
            return None

        # Run decision engine
        decision = self.decision_engine.analyze(
            pair=pair,
            market_data=market_data,
            strategy_weights=strategy_weights,
            current_price=current_price,
        )

        if decision["signal"] not in ("BUY", "SELL"):
            logger.debug(f"No actionable signal for {pair}: {decision['signal']}")
            return None

        signal = decision["signal"]
        df_1h = market_data.get("1h")
        atr = risk_engine.calculate_atr(df_1h) if df_1h is not None else current_price * 0.01
        sl = risk_engine.calculate_stop_loss(current_price, signal, atr)
        tps = risk_engine.calculate_take_profits(current_price, sl, signal)

        # Assume fixed capital placeholder — in production, fetch actual balance
        capital = 1000.0
        quantity = risk_engine.calculate_position_size(capital, current_price, sl)

        snapshot = {
            "pair": pair,
            "signal": signal,
            "confidence": decision["confidence"],
            "market_condition": decision.get("market_condition"),
            "strategies_breakdown": decision.get("strategies_breakdown", []),
            "support_resistance": decision.get("support_resistance", {}),
        }

        mode = settings.TRADING_MODE
        if mode == "REAL":
            trade = await execution_engine.open_trade(
                db, pair, signal, current_price, sl, tps, quantity, snapshot
            )
        else:
            trade = await simulation_engine.open_trade(
                db, pair, signal, current_price, sl, tps, quantity, snapshot
            )

        if trade:
            await telegram_service.notify_trade_opened(trade)

        return trade


# trade_service is instantiated in main.py after the decision engine is built
