"""
PnL Analyzer Types Module
Defines data structures for PnL (Profit and Loss) tracking and analysis
"""

from dataclasses import dataclass, field
from datetime import datetime
from typing import List, Optional, Dict, Literal
from hyperliquid_monitor_plus.core_types import TradeSide


PositionSide = Literal["LONG", "SHORT", "NEUTRAL"]


@dataclass
class Position:
    """
    Represents a position in a specific coin
    
    Attributes:
        coin: The coin/asset symbol
        side: Position side (LONG/SHORT/NEUTRAL)
        size: Current position size
        avg_entry_price: Average entry price
        total_cost: Total cost basis
        trades_count: Number of trades that built this position
        last_updated: Timestamp of last update
    """
    coin: str
    side: PositionSide = "NEUTRAL"
    size: float = 0.0
    avg_entry_price: float = 0.0
    total_cost: float = 0.0
    trades_count: int = 0
    last_updated: Optional[datetime] = None
    
    def __post_init__(self):
        """Validate position data"""
        if self.size < 0:
            raise ValueError(f"Position size cannot be negative: {self.size}")
        if self.avg_entry_price < 0:
            raise ValueError(f"Average entry price cannot be negative: {self.avg_entry_price}")
    
    @property
    def market_value(self) -> float:
        """Calculate current market value based on entry price"""
        return self.size * self.avg_entry_price
    
    def is_open(self) -> bool:
        """Check if position is open"""
        return self.size > 0


@dataclass
class PnLRecord:
    """
    Records a single PnL event (realized profit/loss)
    
    Attributes:
        timestamp: When the PnL was realized
        coin: The coin/asset
        pnl: Profit or loss amount in USD
        trade_side: The trade that realized this PnL
        entry_price: Average entry price
        exit_price: Exit price
        size: Size of the closed position
        fee: Trading fee
        is_full_close: Whether this fully closed the position
    """
    timestamp: datetime
    coin: str
    pnl: float
    trade_side: TradeSide
    entry_price: float
    exit_price: float
    size: float
    fee: float = 0.0
    is_full_close: bool = False
    
    @property
    def pnl_after_fees(self) -> float:
        """PnL after subtracting fees"""
        return self.pnl - self.fee
    
    @property
    def pnl_percentage(self) -> float:
        """Calculate PnL as percentage of entry value"""
        entry_value = self.entry_price * self.size
        if entry_value == 0:
            return 0.0
        return (self.pnl / entry_value) * 100


@dataclass
class CoinPnL:
    """
    Aggregated PnL statistics for a single coin
    
    Attributes:
        coin: The coin/asset symbol
        realized_pnl: Total realized PnL
        unrealized_pnl: Current unrealized PnL
        total_fees: Total fees paid
        total_volume: Total trading volume
        trades_count: Total number of trades
        winning_trades: Number of winning trades
        losing_trades: Number of losing trades
        current_position: Current open position (if any)
        pnl_history: List of PnL records
    """
    coin: str
    realized_pnl: float = 0.0
    unrealized_pnl: float = 0.0
    total_fees: float = 0.0
    total_volume: float = 0.0
    trades_count: int = 0
    winning_trades: int = 0
    losing_trades: int = 0
    current_position: Optional[Position] = None
    pnl_history: List[PnLRecord] = field(default_factory=list)
    
    @property
    def total_pnl(self) -> float:
        """Total PnL (realized + unrealized)"""
        return self.realized_pnl + self.unrealized_pnl
    
    @property
    def net_pnl(self) -> float:
        """Net PnL after fees"""
        return self.total_pnl - self.total_fees
    
    @property
    def win_rate(self) -> float:
        """Calculate win rate as percentage"""
        total = self.winning_trades + self.losing_trades
        if total == 0:
            return 0.0
        return (self.winning_trades / total) * 100
    
    @property
    def average_pnl_per_trade(self) -> float:
        """Calculate average PnL per closed trade"""
        closed_trades = self.winning_trades + self.losing_trades
        if closed_trades == 0:
            return 0.0
        return self.realized_pnl / closed_trades


@dataclass
class PortfolioPnL:
    """
    Overall portfolio PnL summary
    
    Attributes:
        total_realized_pnl: Sum of all realized PnL across all coins
        total_unrealized_pnl: Sum of all unrealized PnL
        total_fees: Sum of all fees paid
        total_volume: Total trading volume
        coin_pnls: Dictionary of CoinPnL for each traded coin
        last_updated: Timestamp of last update
    """
    total_realized_pnl: float = 0.0
    total_unrealized_pnl: float = 0.0
    total_fees: float = 0.0
    total_volume: float = 0.0
    coin_pnls: Dict[str, CoinPnL] = field(default_factory=dict)
    last_updated: Optional[datetime] = None
    
    @property
    def total_pnl(self) -> float:
        """Total PnL (realized + unrealized)"""
        return self.total_realized_pnl + self.total_unrealized_pnl
    
    @property
    def net_pnl(self) -> float:
        """Net PnL after all fees"""
        return self.total_pnl - self.total_fees
    
    @property
    def total_trades(self) -> int:
        """Total number of trades across all coins"""
        return sum(cp.trades_count for cp in self.coin_pnls.values())
    
    @property
    def active_positions_count(self) -> int:
        """Number of currently open positions"""
        return sum(
            1 for cp in self.coin_pnls.values() 
            if cp.current_position and cp.current_position.is_open()
        )
    
    def get_best_performer(self) -> Optional[str]:
        """Get the coin with highest realized PnL"""
        if not self.coin_pnls:
            return None
        return max(self.coin_pnls.keys(), key=lambda c: self.coin_pnls[c].realized_pnl)
    
    def get_worst_performer(self) -> Optional[str]:
        """Get the coin with lowest realized PnL"""
        if not self.coin_pnls:
            return None
        return min(self.coin_pnls.keys(), key=lambda c: self.coin_pnls[c].realized_pnl)
