"""
Risk interception rules for blocking high-risk operations
"""

from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import List, Dict, Any, Optional
from datetime import datetime

from ..models.decision import ImprovementPlan
from ..models.performance import PeriodMetrics
from .controller import RiskConfig, ModificationRecord


@dataclass
class RiskContext:
    """
    Context information for risk evaluation

    Attributes:
        improvement_plan: Improvement plan to evaluate
        current_strategy: Current strategy configuration
        current_metrics: Current performance metrics
        new_metrics: New performance metrics (if available)
        recent_modifications: Recent modification history
        live_pnl: Live trading PnL (if available)
    """

    improvement_plan: ImprovementPlan
    current_strategy: Dict[str, Any]
    current_metrics: PeriodMetrics
    new_metrics: Optional[PeriodMetrics] = None
    recent_modifications: List[ModificationRecord] = field(default_factory=list)
    live_pnl: Optional[float] = None


@dataclass
class RuleViolation:
    """
    Risk rule violation result

    Attributes:
        rule_name: Name of the violated rule
        severity: Violation severity
        description: Violation description
        requires_review: Whether human review is required
        can_proceed: Whether operation can proceed
    """

    rule_name: str
    severity: str  # 'low' | 'medium' | 'high' | 'critical'
    description: str
    requires_review: bool
    can_proceed: bool


@dataclass
class RiskDecision:
    """
    Final risk decision based on all rule evaluations

    Attributes:
        action: Recommended action
        violations: List of rule violations
        reason: Decision reason
        recommendations: List of recommendations
    """

    action: str  # 'proceed' | 'review' | 'block' | 'rollback'
    violations: List[RuleViolation]
    reason: str
    recommendations: List[str]


class RiskRule(ABC):
    """
    Abstract base class for risk rules
    """

    def __init__(self, config: RiskConfig):
        """
        Initialize risk rule

        Args:
            config: Risk configuration
        """
        self.config = config

    @abstractmethod
    def check(self, context: RiskContext) -> RuleViolation:
        """
        Check if rule is violated

        Args:
            context: Risk context

        Returns:
            Rule violation result
        """
        pass


class CoreLogicModificationRule(RiskRule):
    """
    Rule to check if core logic modules are being modified
    """

    def check(self, context: RiskContext) -> RuleViolation:
        """
        Check if core logic is being modified

        Args:
            context: Risk context

        Returns:
            Rule violation result
        """
        affected_modules = []

        for mod in context.improvement_plan.modifications:
            for core_module in self.config.core_modules:
                if core_module in mod.file_path:
                    affected_modules.append(core_module)

        if affected_modules:
            return RuleViolation(
                rule_name="CoreLogicModification",
                severity="high",
                description=f"Attempting to modify core modules: {', '.join(set(affected_modules))}",
                requires_review=True,
                can_proceed=False,
            )

        return RuleViolation(
            rule_name="CoreLogicModification",
            severity="low",
            description="No core logic modifications detected",
            requires_review=False,
            can_proceed=True,
        )


class PerformanceDeclineRule(RiskRule):
    """
    Rule to check if performance significantly declines
    """

    def check(self, context: RiskContext) -> RuleViolation:
        """
        Check if performance declines significantly

        Args:
            context: Risk context

        Returns:
            Rule violation result
        """
        if context.new_metrics is None:
            return RuleViolation(
                rule_name="PerformanceDecline",
                severity="low",
                description="No new metrics available for comparison",
                requires_review=False,
                can_proceed=True,
            )

        current_sharpe = context.current_metrics.sharpe_ratio
        new_sharpe = context.new_metrics.sharpe_ratio

        # Calculate decline percentage
        if current_sharpe > 0:
            decline = (current_sharpe - new_sharpe) / current_sharpe
        else:
            decline = 0.0

        if decline > self.config.performance_decline_threshold:
            return RuleViolation(
                rule_name="PerformanceDecline",
                severity="critical",
                description=(
                    f"Sharpe ratio declined by {decline*100:.1f}%, "
                    f"exceeding threshold of {self.config.performance_decline_threshold*100:.1f}%"
                ),
                requires_review=True,
                can_proceed=False,
            )

        return RuleViolation(
            rule_name="PerformanceDecline",
            severity="low",
            description="No significant performance decline detected",
            requires_review=False,
            can_proceed=True,
        )


class OverfittingRule(RiskRule):
    """
    Rule to detect overfitting
    """

    def check(self, context: RiskContext) -> RuleViolation:
        """
        Check for overfitting indicators

        Args:
            context: Risk context

        Returns:
            Rule violation result
        """
        # Check if we have both in-sample and out-of-sample metrics
        # For now, we'll use a simplified check based on metrics consistency
        if context.new_metrics is None:
            return RuleViolation(
                rule_name="Overfitting",
                severity="low",
                description="Insufficient data for overfitting detection",
                requires_review=False,
                can_proceed=True,
            )

        # Check for suspiciously high performance improvements
        current_sharpe = context.current_metrics.sharpe_ratio
        new_sharpe = context.new_metrics.sharpe_ratio

        if current_sharpe > 0:
            improvement = (new_sharpe - current_sharpe) / current_sharpe
        else:
            improvement = 0.0

        # If improvement is too good to be true (>100%), flag as potential overfitting
        if improvement > 1.0:
            return RuleViolation(
                rule_name="Overfitting",
                severity="high",
                description=(
                    f"Suspiciously high performance improvement ({improvement*100:.1f}%) "
                    "may indicate overfitting"
                ),
                requires_review=True,
                can_proceed=True,  # Allow but require review
            )

        # Check win rate - if too high, might be overfitted
        if context.new_metrics.win_rate > 0.85:
            return RuleViolation(
                rule_name="Overfitting",
                severity="medium",
                description=f"Very high win rate ({context.new_metrics.win_rate*100:.1f}%) may indicate overfitting",
                requires_review=True,
                can_proceed=True,
            )

        return RuleViolation(
            rule_name="Overfitting",
            severity="low",
            description="No obvious overfitting indicators detected",
            requires_review=False,
            can_proceed=True,
        )


class IterationFrequencyRule(RiskRule):
    """
    Rule to check iteration frequency limits
    """

    def check(self, context: RiskContext) -> RuleViolation:
        """
        Check if iteration frequency is within limits

        Args:
            context: Risk context

        Returns:
            Rule violation result
        """
        # Get today's start timestamp
        today_start = int(
            datetime.now().replace(hour=0, minute=0, second=0, microsecond=0).timestamp()
        )

        # Count modifications today
        today_mods = [mod for mod in context.recent_modifications if mod.timestamp >= today_start]

        if len(today_mods) >= self.config.max_iterations_per_day:
            return RuleViolation(
                rule_name="IterationFrequency",
                severity="medium",
                description=(
                    f"Already performed {len(today_mods)} iterations today, "
                    f"reached limit of {self.config.max_iterations_per_day}"
                ),
                requires_review=False,
                can_proceed=False,
            )

        return RuleViolation(
            rule_name="IterationFrequency",
            severity="low",
            description=f"Iteration frequency normal ({len(today_mods)}/{self.config.max_iterations_per_day} today)",
            requires_review=False,
            can_proceed=True,
        )


class LiveLossRule(RiskRule):
    """
    Rule to check live trading losses
    """

    def check(self, context: RiskContext) -> RuleViolation:
        """
        Check if live trading losses exceed threshold

        Args:
            context: Risk context

        Returns:
            Rule violation result
        """
        if context.live_pnl is None:
            return RuleViolation(
                rule_name="LiveLoss",
                severity="low",
                description="No live trading data available",
                requires_review=False,
                can_proceed=True,
            )

        if context.live_pnl < -self.config.live_loss_threshold:
            return RuleViolation(
                rule_name="LiveLoss",
                severity="critical",
                description=(
                    f"Live trading loss {abs(context.live_pnl)*100:.1f}% "
                    f"exceeds threshold {self.config.live_loss_threshold*100:.1f}%"
                ),
                requires_review=True,
                can_proceed=False,
            )

        return RuleViolation(
            rule_name="LiveLoss",
            severity="low",
            description="Live trading performance within acceptable range",
            requires_review=False,
            can_proceed=True,
        )


class RiskInterceptor:
    """
    Risk interceptor that evaluates all risk rules and makes decisions
    """

    def __init__(self, config: RiskConfig):
        """
        Initialize risk interceptor

        Args:
            config: Risk configuration
        """
        self.config = config
        self.rules = self._init_rules()

    def _init_rules(self) -> List[RiskRule]:
        """
        Initialize all risk rules

        Returns:
            List of risk rules
        """
        return [
            CoreLogicModificationRule(self.config),
            PerformanceDeclineRule(self.config),
            OverfittingRule(self.config),
            IterationFrequencyRule(self.config),
            LiveLossRule(self.config),
        ]

    def evaluate(self, context: RiskContext) -> RiskDecision:
        """
        Evaluate all risk rules and make a decision

        Args:
            context: Risk context

        Returns:
            Risk decision
        """
        violations = []

        # Check all rules
        for rule in self.rules:
            result = rule.check(context)
            if result.severity != "low" or not result.can_proceed:
                violations.append(result)

        # Make decision based on violations
        return self._make_decision(violations)

    def _make_decision(self, violations: List[RuleViolation]) -> RiskDecision:
        """
        Make risk decision based on violations

        Args:
            violations: List of rule violations

        Returns:
            Risk decision
        """
        if not violations:
            return RiskDecision(
                action="proceed",
                violations=[],
                reason="No risk violations detected",
                recommendations=["Proceed with modification"],
            )

        # Check for critical violations
        critical_violations = [v for v in violations if v.severity == "critical"]
        if critical_violations:
            # Check if any critical violation blocks proceeding
            blocking_violations = [v for v in critical_violations if not v.can_proceed]
            if blocking_violations:
                return RiskDecision(
                    action="block",
                    violations=violations,
                    reason="Critical risk violations detected that block proceeding",
                    recommendations=[
                        "Review and address critical issues before proceeding",
                        "Consider rolling back recent changes",
                        "Consult with human operator",
                    ],
                )

        # Check if any violation requires review
        review_required = any(v.requires_review for v in violations)
        if review_required:
            return RiskDecision(
                action="review",
                violations=violations,
                reason="Risk violations require human review",
                recommendations=[
                    "Submit for human review",
                    "Provide detailed analysis of risks",
                    "Wait for approval before proceeding",
                ],
            )

        # Check if any violation blocks proceeding
        blocking_violations = [v for v in violations if not v.can_proceed]
        if blocking_violations:
            return RiskDecision(
                action="block",
                violations=violations,
                reason="Risk violations block proceeding",
                recommendations=[
                    "Address blocking issues",
                    "Adjust modification plan",
                    "Retry after resolving issues",
                ],
            )

        # Medium severity violations - proceed with caution
        return RiskDecision(
            action="proceed",
            violations=violations,
            reason="Minor risk violations detected, but can proceed with caution",
            recommendations=[
                "Monitor closely during deployment",
                "Be prepared to rollback if issues arise",
                "Document all changes carefully",
            ],
        )
