from __future__ import annotations

from dataclasses import asdict, dataclass, field
from decimal import Decimal
from enum import Enum
from typing import Any, Literal

LeaderType = Literal["wallet", "vault"]
Side = Literal["long", "short", "flat"]


class ShadowDeltaType(str, Enum):
    NEW_POSITION = "new_position"
    INCREASE_POSITION = "increase_position"
    DECREASE_POSITION = "decrease_position"
    CLOSE_POSITION = "close_position"
    FLIP_POSITION = "flip_position"
    UNCHANGED_POSITION = "unchanged_position"


class ShadowDecisionType(str, Enum):
    ALLOWED = "allowed"
    BLOCKED = "blocked"
    IGNORED = "ignored"


@dataclass(frozen=True)
class ShadowLeaderPosition:
    leader_id: str
    leader_type: LeaderType
    symbol: str
    side: Side
    size: Decimal
    leader_entry_price: Decimal
    current_mid: Decimal
    observed_at: str
    position_age_hours: Decimal = Decimal("0")
    leader_unrealized_pnl_pct: Decimal = Decimal("0")
    leader_liquidation_distance_pct: Decimal = Decimal("999")
    funding_rate: Decimal = Decimal("0")
    spread_pct: Decimal = Decimal("0")
    top_depth_usd: Decimal = Decimal("999999999")
    source_snapshot_id: str | None = None


@dataclass(frozen=True)
class ShadowPositionDelta:
    leader_id: str
    leader_type: LeaderType
    symbol: str
    side: Side
    previous_size: Decimal
    current_size: Decimal
    size_delta: Decimal
    delta_type: ShadowDeltaType
    leader_entry_price: Decimal
    current_mid: Decimal
    observed_at: str
    position_age_hours: Decimal
    leader_unrealized_pnl_pct: Decimal
    leader_liquidation_distance_pct: Decimal
    funding_rate: Decimal
    spread_pct: Decimal
    top_depth_usd: Decimal
    source_snapshot_ids: tuple[str, ...] = ()


@dataclass(frozen=True)
class ShadowCostEstimate:
    entry_fee_pct: Decimal
    exit_fee_pct: Decimal
    spread_cost_pct: Decimal
    modeled_slippage_pct: Decimal
    delay_penalty_pct: Decimal
    estimated_total_entry_cost_pct: Decimal
    estimated_roundtrip_cost_pct: Decimal


@dataclass(frozen=True)
class ShadowCopyDecision:
    leader_id: str
    leader_type: LeaderType
    symbol: str
    side: Side
    previous_size: Decimal
    current_size: Decimal
    size_delta: Decimal
    delta_type: ShadowDeltaType
    decision: ShadowDecisionType
    reason_codes: tuple[str, ...]
    leader_entry_price: Decimal
    current_mid: Decimal
    observed_at: str
    position_age_hours: Decimal
    leader_unrealized_pnl_pct: Decimal
    leader_liquidation_distance_pct: Decimal
    funding_rate: Decimal
    spread_pct: Decimal
    top_depth_usd: Decimal
    follower_sim_entry_price: Decimal | None
    estimated_roundtrip_cost_pct: Decimal
    estimated_slippage_pct: Decimal
    estimated_delay_penalty_pct: Decimal
    estimated_total_entry_cost_pct: Decimal
    risk_unit_R: Decimal
    estimated_costs: ShadowCostEstimate
    source_snapshot_ids: tuple[str, ...] = ()
    read_only_guard: dict[str, bool] = field(default_factory=dict)

    def to_json_dict(self) -> dict[str, Any]:
        def convert(value: Any) -> Any:
            if isinstance(value, Decimal):
                return str(value)
            if isinstance(value, Enum):
                return value.value
            if isinstance(value, tuple):
                return [convert(v) for v in value]
            if hasattr(value, "__dataclass_fields__"):
                return {k: convert(v) for k, v in asdict(value).items()}
            if isinstance(value, dict):
                return {str(k): convert(v) for k, v in value.items()}
            return value
        return convert(asdict(self))


@dataclass(frozen=True)
class ShadowFollowerPosition:
    leader_id: str
    leader_type: LeaderType
    symbol: str
    side: Side
    simulated_size: Decimal
    follower_sim_entry_price: Decimal
    notional_usd: Decimal
    risk_unit_R: Decimal
    opened_at: str
    source_decision: ShadowCopyDecision

    def to_json_dict(self) -> dict[str, Any]:
        return {
            "leader_id": self.leader_id,
            "leader_type": self.leader_type,
            "symbol": self.symbol,
            "side": self.side,
            "simulated_size": str(self.simulated_size),
            "follower_sim_entry_price": str(self.follower_sim_entry_price),
            "notional_usd": str(self.notional_usd),
            "risk_unit_R": str(self.risk_unit_R),
            "opened_at": self.opened_at,
            "source_decision": self.source_decision.to_json_dict(),
        }


@dataclass(frozen=True)
class ShadowPortfolioState:
    observed_at: str
    positions: tuple[ShadowFollowerPosition, ...]
    total_notional_usd: Decimal
    read_only_guard: dict[str, bool]

    def to_json_dict(self) -> dict[str, Any]:
        return {
            "observed_at": self.observed_at,
            "positions": [p.to_json_dict() for p in self.positions],
            "total_notional_usd": str(self.total_notional_usd),
            "read_only_guard": self.read_only_guard,
        }


@dataclass(frozen=True)
class ShadowRunSummary:
    status: str
    day: str
    decisions_count: int
    allowed_count: int
    blocked_count: int
    ignored_count: int
    top_block_reasons: dict[str, int]
    symbols_observed: tuple[str, ...]
    leaders_observed: tuple[str, ...]
    estimated_total_notional_allowed: Decimal
    decision_journal_path: str
    portfolio_journal_path: str
    result_path: str
    read_only_guard: dict[str, bool]

    def to_json_dict(self) -> dict[str, Any]:
        return {
            "status": self.status,
            "day": self.day,
            "decisions_count": self.decisions_count,
            "allowed_count": self.allowed_count,
            "blocked_count": self.blocked_count,
            "ignored_count": self.ignored_count,
            "top_block_reasons": self.top_block_reasons,
            "symbols_observed": list(self.symbols_observed),
            "leaders_observed": list(self.leaders_observed),
            "estimated_total_notional_allowed": str(self.estimated_total_notional_allowed),
            "decision_journal_path": self.decision_journal_path,
            "portfolio_journal_path": self.portfolio_journal_path,
            "result_path": self.result_path,
            "read_only_guard": self.read_only_guard,
        }
