"""Portfolio rebalancer for the autonomous trading engine.

Evaluates portfolio concentration and generates rebalancing sell orders
when single-stock or sector exposure exceeds configured limits.
"""

from __future__ import annotations

from dataclasses import dataclass

from services.trading.models import OpenPosition, RiskTierConfig


@dataclass
class RebalanceOrder:
    """A sell order generated by the portfolio rebalancer."""

    ticker: str
    action: str = "sell"
    quantity: int = 0
    reason: str = ""
    tag: str = "rebalance"


class PortfolioRebalancer:
    """Evaluates portfolio concentration and generates rebalancing orders.

    Generates partial sell orders when:
    - A single stock exceeds max_position_pct of the active pool
    - A sector exceeds max_sector_pct of the active pool
    - The number of open positions exceeds the configured maximum
    """

    def evaluate(
        self,
        positions: list[OpenPosition],
        risk_tier: RiskTierConfig,
        active_pool: float,
        max_positions: int = 10,
    ) -> list[RebalanceOrder]:
        """Evaluate portfolio and generate rebalancing sell orders.

        Args:
            positions: Current open positions.
            risk_tier: Active risk tier configuration.
            active_pool: Current active pool value in dollars.
            max_positions: Maximum allowed open positions.

        Returns:
            List of RebalanceOrder for positions that need trimming.
        """
        orders: list[RebalanceOrder] = []
        if not positions or active_pool <= 0:
            return orders

        # Track which tickers already have orders to avoid duplicates
        ordered_tickers: dict[str, RebalanceOrder] = {}

        # --- 1. Single-stock concentration check ---
        max_position_dollars = risk_tier.max_position_pct * active_pool
        for pos in positions:
            if pos.market_value > max_position_dollars and pos.current_price > 0:
                excess = pos.market_value - max_position_dollars
                sell_qty = int(excess / pos.current_price)
                if sell_qty > 0:
                    sell_qty = min(sell_qty, pos.quantity)
                    order = RebalanceOrder(
                        ticker=pos.ticker,
                        action="sell",
                        quantity=sell_qty,
                        reason=(
                            f"Position {pos.ticker} market_value "
                            f"${pos.market_value:.2f} exceeds "
                            f"max_position_pct limit "
                            f"${max_position_dollars:.2f}"
                        ),
                        tag="rebalance",
                    )
                    ordered_tickers[pos.ticker] = order
                    orders.append(order)

        # --- 2. Sector concentration check ---
        max_sector_dollars = risk_tier.max_sector_pct * active_pool

        # Group positions by sector
        sector_positions: dict[str, list[OpenPosition]] = {}
        for pos in positions:
            sector_positions.setdefault(pos.sector, []).append(pos)

        for sector, sector_pos in sector_positions.items():
            sector_value = sum(p.market_value for p in sector_pos)
            if sector_value > max_sector_dollars:
                excess = sector_value - max_sector_dollars
                # Sort by confidence ascending — sell lowest confidence first
                sorted_pos = sorted(sector_pos, key=lambda p: p.signal_confidence)
                remaining_excess = excess

                for pos in sorted_pos:
                    if remaining_excess <= 0:
                        break
                    if pos.current_price <= 0:
                        continue

                    # Determine how many shares to sell from this position
                    sell_value = min(remaining_excess, pos.market_value)
                    sell_qty = int(sell_value / pos.current_price)
                    if sell_qty <= 0:
                        continue
                    sell_qty = min(sell_qty, pos.quantity)

                    if pos.ticker in ordered_tickers:
                        # Already have an order for this ticker — take the larger
                        existing = ordered_tickers[pos.ticker]
                        if sell_qty > existing.quantity:
                            existing.quantity = sell_qty
                            existing.reason += (
                                f"; also sector {sector} exposure "
                                f"${sector_value:.2f} exceeds limit "
                                f"${max_sector_dollars:.2f}"
                            )
                    else:
                        order = RebalanceOrder(
                            ticker=pos.ticker,
                            action="sell",
                            quantity=sell_qty,
                            reason=(
                                f"Sector {sector} exposure "
                                f"${sector_value:.2f} exceeds "
                                f"max_sector_pct limit "
                                f"${max_sector_dollars:.2f} — "
                                f"selling lowest-confidence position"
                            ),
                            tag="rebalance",
                        )
                        ordered_tickers[pos.ticker] = order
                        orders.append(order)

                    remaining_excess -= sell_qty * pos.current_price

        # --- 3. Maximum open positions enforcement ---
        if len(positions) > max_positions:
            excess_count = len(positions) - max_positions
            # Sort by confidence ascending — sell lowest confidence first
            sorted_all = sorted(positions, key=lambda p: p.signal_confidence)

            sold_count = 0
            for pos in sorted_all:
                if sold_count >= excess_count:
                    break

                if pos.ticker in ordered_tickers:
                    # Already selling this ticker — count it toward excess
                    existing = ordered_tickers[pos.ticker]
                    if existing.quantity < pos.quantity:
                        existing.quantity = pos.quantity
                        existing.reason += "; also exceeds max open positions"
                    sold_count += 1
                else:
                    order = RebalanceOrder(
                        ticker=pos.ticker,
                        action="sell",
                        quantity=pos.quantity,
                        reason=(
                            f"Portfolio has {len(positions)} positions, "
                            f"exceeding max of {max_positions} — "
                            f"selling lowest-confidence position"
                        ),
                        tag="rebalance",
                    )
                    ordered_tickers[pos.ticker] = order
                    orders.append(order)
                    sold_count += 1

        return orders
