"""Canary signal influence — paper trading with v3 signals.

Enables v3 signals in paper trading at a small percentage, tracks
extraction correctness separately from trading outcomes, reviews
material recommendation divergences, and requires explicit owner
approval for full promotion.
"""

from __future__ import annotations

import enum
from dataclasses import dataclass, field
from datetime import datetime, timezone
from typing import Any
from uuid import UUID, uuid4


class PromotionStatus(str, enum.Enum):
    """Status of the canary promotion process."""

    PENDING = "pending"
    PAPER_TRADING = "paper_trading"
    AWAITING_REVIEW = "awaiting_review"
    APPROVED = "approved"
    REJECTED = "rejected"


@dataclass
class DivergenceRecord:
    """Record of a material recommendation divergence between v2 and v3."""

    record_id: UUID
    document_id: str
    timestamp: datetime
    v2_recommendation: dict[str, Any]
    v3_recommendation: dict[str, Any]
    divergence_type: str  # e.g., "direction_opposite", "magnitude_significant"
    impact_estimate: float = 0.0  # Estimated impact on portfolio
    reviewed: bool = False
    reviewer_notes: str = ""

    @classmethod
    def create(
        cls,
        document_id: str,
        v2_recommendation: dict[str, Any],
        v3_recommendation: dict[str, Any],
        divergence_type: str,
        impact_estimate: float = 0.0,
    ) -> DivergenceRecord:
        return cls(
            record_id=uuid4(),
            document_id=document_id,
            timestamp=datetime.now(timezone.utc),
            v2_recommendation=v2_recommendation,
            v3_recommendation=v3_recommendation,
            divergence_type=divergence_type,
            impact_estimate=impact_estimate,
        )


@dataclass
class SignalInfluenceConfig:
    """Configuration for canary signal influence in paper trading."""

    enabled: bool = False
    percentage: int = 5  # Start at 5% of paper trading signals
    require_owner_approval: bool = True
    owner_id: str = ""

    # Reporting thresholds
    material_divergence_threshold: float = 0.20
    max_divergence_rate: float = 0.15

    # Separation of concerns
    report_extraction_separately: bool = True
    report_trading_separately: bool = True


@dataclass
class SignalInfluenceTracker:
    """Tracks canary signal influence in paper trading.

    Reports extraction correctness separately from trading outcomes.
    Reviews material divergences and tracks promotion readiness.
    """

    config: SignalInfluenceConfig
    promotion_status: PromotionStatus = PromotionStatus.PENDING
    _divergences: list[DivergenceRecord] = field(default_factory=list)
    _extraction_metrics: dict[str, float] = field(default_factory=dict)
    _trading_metrics: dict[str, float] = field(default_factory=dict)
    _total_signals: int = 0
    _v3_signals: int = 0
    _approval_timestamp: datetime | None = None
    _approver_id: str = ""

    def start_paper_trading(self) -> None:
        """Begin paper trading with v3 signals."""
        self.config.enabled = True
        self.promotion_status = PromotionStatus.PAPER_TRADING

    def record_signal(self, is_v3: bool = False) -> None:
        """Record a signal processed."""
        self._total_signals += 1
        if is_v3:
            self._v3_signals += 1

    def record_divergence(self, divergence: DivergenceRecord) -> None:
        """Record a material recommendation divergence."""
        self._divergences.append(divergence)

    def update_extraction_metrics(self, metrics: dict[str, float]) -> None:
        """Update extraction correctness metrics (separate from trading)."""
        self._extraction_metrics.update(metrics)

    def update_trading_metrics(self, metrics: dict[str, float]) -> None:
        """Update trading outcome metrics (separate from extraction)."""
        self._trading_metrics.update(metrics)

    @property
    def divergence_rate(self) -> float:
        if self._v3_signals == 0:
            return 0.0
        return len(self._divergences) / self._v3_signals

    @property
    def unreviewed_divergences(self) -> list[DivergenceRecord]:
        return [d for d in self._divergences if not d.reviewed]

    def request_approval(self) -> None:
        """Move to awaiting review status."""
        self.promotion_status = PromotionStatus.AWAITING_REVIEW

    def approve(self, approver_id: str) -> bool:
        """Approve promotion. Requires owner approval if configured.

        Returns False if approval requirements are not met.
        """
        if self.config.require_owner_approval:
            if not approver_id:
                return False
            if self.config.owner_id and approver_id != self.config.owner_id:
                return False

        # Check all gates
        if not self._all_gates_pass():
            return False

        self.promotion_status = PromotionStatus.APPROVED
        self._approval_timestamp = datetime.now(timezone.utc)
        self._approver_id = approver_id
        return True

    def reject(self, reason: str = "") -> None:
        """Reject promotion."""
        self.promotion_status = PromotionStatus.REJECTED

    def _all_gates_pass(self) -> bool:
        """Check if extraction correctness gates pass.

        Trading outcomes explicitly do NOT override correctness gates
        (Requirement 16.10).
        """
        # Divergence rate must be below threshold
        if self.divergence_rate > self.config.max_divergence_rate:
            return False

        # All divergences must be reviewed
        if self.unreviewed_divergences:
            return False

        return True

    def summary(self) -> dict[str, Any]:
        return {
            "enabled": self.config.enabled,
            "status": self.promotion_status.value,
            "percentage": self.config.percentage,
            "total_signals": self._total_signals,
            "v3_signals": self._v3_signals,
            "divergence_count": len(self._divergences),
            "divergence_rate": self.divergence_rate,
            "unreviewed_divergences": len(self.unreviewed_divergences),
            "extraction_metrics": self._extraction_metrics,
            "trading_metrics": self._trading_metrics,
        }
