"""Fund accounting: fee tracking and P&L attribution."""

from __future__ import annotations

import logging
import time
from collections import deque
from dataclasses import dataclass, field
from typing import Any

from horizon.fund._types import FeeType, FundConfig, NAVSnapshot

logger = logging.getLogger("horizon.fund.accounting")


@dataclass
class FeeRecord:
    """A single fee event."""
    timestamp: float
    fee_type: FeeType
    amount: float
    strategy: str
    description: str = ""


class FundAccounting:
    """Tracks fees, P&L attribution, and fund-level accounting.

    Computes management fees (time-based, annual) and performance fees
    (profit share above HWM).
    """

    def __init__(self, config: FundConfig, min_fee_interval_secs: float = 3600.0) -> None:
        self._config = config
        self._fees: deque[FeeRecord] = deque(maxlen=50_000)
        self._last_mgmt_fee_time: float = time.time()
        self._performance_fee_hwm: float = config.total_capital
        self._min_fee_interval_secs: float = min_fee_interval_secs

    def record_fee(
        self,
        fee_type: FeeType,
        amount: float,
        strategy: str,
        description: str = "",
    ) -> None:
        """Record a fee event."""
        self._fees.append(FeeRecord(
            timestamp=time.time(),
            fee_type=fee_type,
            amount=amount,
            strategy=strategy,
            description=description,
        ))

    def compute_management_fee(self, nav: NAVSnapshot) -> float:
        """Compute accrued management fee since last computation.

        Management fee = NAV * (bps/10000) * (elapsed_days/365)

        Returns:
            Fee amount accrued.
        """
        now = time.time()
        elapsed_secs = now - self._last_mgmt_fee_time

        # Fee batching: only accrue if enough time has passed
        if elapsed_secs < self._min_fee_interval_secs:
            return 0.0

        elapsed_days = elapsed_secs / 86400.0

        annual_rate = self._config.management_fee_bps / 10_000.0
        fee = nav.total_nav * annual_rate * (elapsed_days / 365.0)

        if fee > 0:
            self.record_fee(
                FeeType.MANAGEMENT, fee, "fund",
                f"Management fee for {elapsed_days:.2f} days",
            )
            self._last_mgmt_fee_time = now

        return fee

    def compute_performance_fee(self, nav: NAVSnapshot) -> float:
        """Compute performance fee on profits above HWM.

        Performance fee = (NAV - HWM) * fee_pct/100, only if NAV > HWM.

        Returns:
            Fee amount (0 if no new profits).
        """
        if nav.total_nav <= self._performance_fee_hwm:
            return 0.0

        profit = nav.total_nav - self._performance_fee_hwm
        fee = profit * (self._config.performance_fee_pct / 100.0)

        if fee > 0:
            self.record_fee(
                FeeType.PERFORMANCE, fee, "fund",
                f"Performance fee on {profit:.2f} profit above HWM",
            )
            self._performance_fee_hwm = nav.total_nav

        return fee

    def total_fees(self, fee_type: FeeType | None = None) -> float:
        """Total fees collected, optionally filtered by type."""
        if fee_type is None:
            return sum(f.amount for f in self._fees)
        return sum(f.amount for f in self._fees if f.fee_type == fee_type)

    def fees_by_strategy(self) -> dict[str, float]:
        """Total fees broken down by strategy."""
        result: dict[str, float] = {}
        for f in self._fees:
            result[f.strategy] = result.get(f.strategy, 0.0) + f.amount
        return result

    def recent_fees(self, limit: int = 50) -> list[FeeRecord]:
        """Return recent fee records."""
        return list(self._fees)[-limit:]

    def pnl_attribution(self, engines: dict[str, Any]) -> dict[str, dict[str, float]]:
        """Compute P&L attribution per strategy.

        Returns:
            Mapping of strategy_name -> {"realized": x, "unrealized": y, "total": z, "fees": f}
        """
        result: dict[str, dict[str, float]] = {}
        strategy_fees = self.fees_by_strategy()

        for name, engine in engines.items():
            try:
                status = engine.status()
                realized = status.total_realized_pnl
                unrealized = status.total_unrealized_pnl
                fees = strategy_fees.get(name, 0.0)
                result[name] = {
                    "realized": realized,
                    "unrealized": unrealized,
                    "total": realized + unrealized,
                    "net": realized + unrealized - fees,
                    "fees": fees,
                }
            except Exception:
                result[name] = {
                    "realized": 0.0,
                    "unrealized": 0.0,
                    "total": 0.0,
                    "net": 0.0,
                    "fees": strategy_fees.get(name, 0.0),
                }

        return result
