"""Advanced portfolio analytics (Sharpe ratio, Sortino, drawdown analysis, performance charts)."""

import numpy as np
import pandas as pd
from decimal import Decimal
from typing import Dict, List, Any, Optional
from datetime import datetime, timedelta
from sqlalchemy import select
from src.core.database import get_database, PortfolioSnapshot, Trade
from src.core.logger import get_logger
from .tracker import get_portfolio_tracker

logger = get_logger(__name__)


class PortfolioAnalytics:
    """Advanced portfolio analytics."""
    
    def __init__(self):
        """Initialize portfolio analytics."""
        self.db = get_database()
        self.tracker = get_portfolio_tracker()
        self.logger = get_logger(__name__)
    
    def calculate_sharpe_ratio(
        self,
        returns: pd.Series,
        risk_free_rate: float = 0.0,
        periods_per_year: int = 252
    ) -> float:
        """Calculate Sharpe ratio.
        
        Args:
            returns: Series of returns
            risk_free_rate: Risk-free rate (annual)
            periods_per_year: Trading periods per year
            
        Returns:
            Sharpe ratio
        """
        if len(returns) == 0 or returns.std() == 0:
            return 0.0
        
        excess_returns = returns - (risk_free_rate / periods_per_year)
        return float(np.sqrt(periods_per_year) * excess_returns.mean() / returns.std())
    
    def calculate_sortino_ratio(
        self,
        returns: pd.Series,
        risk_free_rate: float = 0.0,
        periods_per_year: int = 252
    ) -> float:
        """Calculate Sortino ratio.
        
        Args:
            returns: Series of returns
            risk_free_rate: Risk-free rate (annual)
            periods_per_year: Trading periods per year
            
        Returns:
            Sortino ratio
        """
        if len(returns) == 0:
            return 0.0
        
        excess_returns = returns - (risk_free_rate / periods_per_year)
        downside_returns = returns[returns < 0]
        
        if len(downside_returns) == 0 or downside_returns.std() == 0:
            return 0.0
        
        downside_std = downside_returns.std()
        return float(np.sqrt(periods_per_year) * excess_returns.mean() / downside_std)
    
    def calculate_drawdown(self, values: pd.Series) -> Dict[str, Any]:
        """Calculate drawdown metrics.
        
        Args:
            values: Series of portfolio values
            
        Returns:
            Dictionary with drawdown metrics
        """
        if len(values) == 0:
            return {
                "max_drawdown": 0.0,
                "current_drawdown": 0.0,
                "drawdown_duration": 0,
            }
        
        # Calculate running maximum
        running_max = values.expanding().max()
        drawdown = (values - running_max) / running_max
        
        max_drawdown = float(drawdown.min())
        current_drawdown = float(drawdown.iloc[-1])
        
        # Calculate drawdown duration
        in_drawdown = drawdown < 0
        drawdown_duration = 0
        if in_drawdown.iloc[-1]:
            # Count consecutive days in drawdown
            for i in range(len(in_drawdown) - 1, -1, -1):
                if in_drawdown.iloc[i]:
                    drawdown_duration += 1
                else:
                    break
        
        return {
            "max_drawdown": max_drawdown,
            "current_drawdown": current_drawdown,
            "drawdown_duration": drawdown_duration,
            "drawdown_series": drawdown.tolist(),
        }
    
    async def get_performance_metrics(
        self,
        days: int = 30,
        paper_trading: bool = True
    ) -> Dict[str, Any]:
        """Get comprehensive performance metrics.
        
        Args:
            days: Number of days to analyze
            paper_trading: Paper trading flag
            
        Returns:
            Dictionary of performance metrics
        """
        history = await self.tracker.get_portfolio_history(days, paper_trading)
        
        if len(history) < 2:
            return {
                "total_return": 0.0,
                "sharpe_ratio": 0.0,
                "sortino_ratio": 0.0,
                "max_drawdown": 0.0,
                "win_rate": 0.0,
            }
        
        # Convert to DataFrame
        df = pd.DataFrame(history)
        df['timestamp'] = pd.to_datetime(df['timestamp'])
        df = df.set_index('timestamp').sort_index()
        
        # Calculate returns
        returns = df['total_value'].pct_change().dropna()
        
        # Calculate metrics
        initial_value = df['total_value'].iloc[0]
        final_value = df['total_value'].iloc[-1]
        total_return = (final_value - initial_value) / initial_value
        
        sharpe = self.calculate_sharpe_ratio(returns)
        sortino = self.calculate_sortino_ratio(returns)
        drawdown = self.calculate_drawdown(df['total_value'])
        
        # Calculate win rate from trades
        win_rate = await self._calculate_win_rate(days, paper_trading)
        
        # Calculate fee metrics
        fee_metrics = await self._calculate_fee_metrics(days, paper_trading, initial_value)
        
        return {
            "total_return": float(total_return),
            "total_return_percent": float(total_return * 100),
            "sharpe_ratio": sharpe,
            "sortino_ratio": sortino,
            "max_drawdown": drawdown["max_drawdown"],
            "current_drawdown": drawdown["current_drawdown"],
            "win_rate": win_rate,
            "initial_value": float(initial_value),
            "final_value": float(final_value),
            **fee_metrics,  # Include fee metrics
        }
    
    async def _calculate_fee_metrics(
        self,
        days: int,
        paper_trading: bool,
        initial_value: float
    ) -> Dict[str, float]:
        """Calculate fee-related metrics.
        
        Args:
            days: Number of days
            paper_trading: Paper trading flag
            initial_value: Initial portfolio value
            
        Returns:
            Dictionary of fee metrics
        """
        try:
            async with self.db.get_session() as session:
                since = datetime.utcnow() - timedelta(days=days)
                stmt = select(Trade).where(
                    Trade.paper_trading == paper_trading,
                    Trade.timestamp >= since
                )
                result = await session.execute(stmt)
                trades = result.scalars().all()
                
                total_fees = sum(float(trade.fee or 0) for trade in trades)
                total_trades = len(trades)
                avg_fee_per_trade = total_fees / total_trades if total_trades > 0 else 0.0
                
                # Calculate fee percentage of initial value
                fee_percentage = (total_fees / initial_value * 100) if initial_value > 0 else 0.0
                
                return {
                    "total_fees": total_fees,
                    "avg_fee_per_trade": avg_fee_per_trade,
                    "fee_percentage": fee_percentage,
                    "total_trades_with_fees": total_trades,
                }
        except Exception as e:
            logger.warning(f"Error calculating fee metrics: {e}")
            return {
                "total_fees": 0.0,
                "avg_fee_per_trade": 0.0,
                "fee_percentage": 0.0,
                "total_trades_with_fees": 0,
            }
    
    async def _calculate_win_rate(self, days: int, paper_trading: bool) -> float:
        """Calculate win rate from trades.
        
        Args:
            days: Number of days
            paper_trading: Paper trading flag
            
        Returns:
            Win rate (0.0 to 1.0)
        """
        try:
            async with self.db.get_session() as session:
                since = datetime.utcnow() - timedelta(days=days)
                stmt = select(Trade).where(
                    Trade.paper_trading == paper_trading,
                    Trade.timestamp >= since
                )
                result = await session.execute(stmt)
                trades = result.scalars().all()
                
                if len(trades) == 0:
                    return 0.0
                
                # Simplified win rate calculation
                # In practice, would need to match buy/sell pairs
                return 0.5  # Placeholder
        except Exception as e:
            logger.warning(f"Error calculating win rate: {e}")
            return 0.0


# Global portfolio analytics
_portfolio_analytics: Optional[PortfolioAnalytics] = None


def get_portfolio_analytics() -> PortfolioAnalytics:
    """Get global portfolio analytics instance."""
    global _portfolio_analytics
    if _portfolio_analytics is None:
        _portfolio_analytics = PortfolioAnalytics()
    return _portfolio_analytics
