"""
Risk control module for evaluating and managing modification risks
"""

import time
from dataclasses import dataclass, field
from typing import Dict, List, Any, Optional
from datetime import datetime

from ..models.decision import ImprovementPlan, CodeModification
from ..models.performance import PeriodMetrics


@dataclass
class RiskConfig:
    """
    Risk control configuration

    Attributes:
        max_drawdown_threshold: Maximum drawdown threshold (0-1)
        min_sharpe_ratio: Minimum acceptable Sharpe ratio
        max_iterations_per_day: Maximum iterations allowed per day
        core_modules: List of core module paths that require review
        performance_decline_threshold: Performance decline threshold (0-1)
        overfitting_threshold: Overfitting detection threshold (0-1)
        live_loss_threshold: Live trading loss threshold (0-1)
    """

    max_drawdown_threshold: float = 0.20
    min_sharpe_ratio: float = 0.5
    max_iterations_per_day: int = 1
    core_modules: List[str] = field(default_factory=list)
    performance_decline_threshold: float = 0.15
    overfitting_threshold: float = 0.30
    live_loss_threshold: float = 0.10


@dataclass
class RiskFactor:
    """
    Individual risk factor

    Attributes:
        factor_type: Type of risk factor
        severity: Severity level
        description: Risk description
        mitigation: Suggested mitigation strategy
    """

    factor_type: str  # 'core_logic' | 'performance' | 'frequency' | 'overfitting'
    severity: str  # 'low' | 'medium' | 'high' | 'critical'
    description: str
    mitigation: str


@dataclass
class RiskAssessment:
    """
    Risk assessment result

    Attributes:
        risk_level: Overall risk level
        risk_factors: List of identified risk factors
        requires_review: Whether human review is required
        can_proceed: Whether modification can proceed
        recommendations: List of recommendations
        timestamp: Assessment timestamp
    """

    risk_level: str  # 'low' | 'medium' | 'high' | 'critical'
    risk_factors: List[RiskFactor]
    requires_review: bool
    can_proceed: bool
    recommendations: List[str]
    timestamp: int


@dataclass
class PerformanceComparison:
    """
    Performance comparison between current and new metrics

    Attributes:
        improved: Whether performance improved overall
        metrics_comparison: Dictionary of metric comparisons (old, new)
        significant_decline: Whether there's significant performance decline
        decline_percentage: Percentage of decline
        analysis: Analysis description
    """

    improved: bool
    metrics_comparison: Dict[str, tuple[float, float]]
    significant_decline: bool
    decline_percentage: float
    analysis: str


@dataclass
class OverfittingAnalysis:
    """
    Overfitting detection analysis

    Attributes:
        is_overfitted: Whether overfitting is detected
        in_sample_sharpe: In-sample Sharpe ratio
        out_sample_sharpe: Out-of-sample Sharpe ratio
        performance_gap: Performance gap between samples
        confidence: Confidence level of detection
        recommendations: List of recommendations
    """

    is_overfitted: bool
    in_sample_sharpe: float
    out_sample_sharpe: float
    performance_gap: float
    confidence: float
    recommendations: List[str]


@dataclass
class FrequencyCheck:
    """
    Iteration frequency check result

    Attributes:
        within_limit: Whether within frequency limit
        modifications_today: Number of modifications today
        max_allowed: Maximum allowed modifications
        next_allowed_time: Next allowed modification timestamp
    """

    within_limit: bool
    modifications_today: int
    max_allowed: int
    next_allowed_time: int


@dataclass
class LivePerformanceCheck:
    """
    Live performance check result

    Attributes:
        within_threshold: Whether within acceptable threshold
        current_pnl_percent: Current PnL percentage
        threshold_percent: Threshold percentage
        should_rollback: Whether rollback is recommended
        reason: Reason for the check result
    """

    within_threshold: bool
    current_pnl_percent: float
    threshold_percent: float
    should_rollback: bool
    reason: str


@dataclass
class ModificationRecord:
    """
    Record of a code modification

    Attributes:
        modification_id: Unique modification identifier
        timestamp: Modification timestamp
        plan: Improvement plan that was applied
        result: Modification result
        validation: Validation report
        risk_assessment: Risk assessment for the modification
    """

    modification_id: str
    timestamp: int
    plan: ImprovementPlan
    result: Any  # ModificationResult
    validation: Any  # ValidationReport
    risk_assessment: RiskAssessment


class RiskController:
    """
    Risk controller for evaluating and managing modification risks

    This class implements multiple risk control mechanisms to prevent
    high-risk operations and ensure system stability.
    """

    def __init__(self, config: RiskConfig, notification_service: Any):
        """
        Initialize risk controller

        Args:
            config: Risk control configuration
            notification_service: Notification service for alerts
        """
        self.config = config
        self.notifier = notification_service
        self.risk_history: List[RiskAssessment] = []

    def evaluate_modification_risk(
        self, plan: ImprovementPlan, current_strategy: Dict[str, Any]
    ) -> RiskAssessment:
        """
        Evaluate risk of a code modification plan

        Args:
            plan: Improvement plan to evaluate
            current_strategy: Current strategy configuration

        Returns:
            Risk assessment result
        """
        risk_factors = []
        recommendations = []

        # Check if core logic is being modified
        core_logic_risk = self.check_core_logic_modification(plan.modifications)
        if core_logic_risk:
            risk_factors.append(
                RiskFactor(
                    factor_type="core_logic",
                    severity="high",
                    description=f"Modification affects core modules: {core_logic_risk}",
                    mitigation="Require human review before proceeding",
                )
            )
            recommendations.append("Request human review for core logic changes")

        # Evaluate plan risk level
        if plan.risk_level == "high":
            risk_factors.append(
                RiskFactor(
                    factor_type="plan_risk",
                    severity="high",
                    description="Improvement plan marked as high risk",
                    mitigation="Thorough testing and validation required",
                )
            )
            recommendations.append("Perform extensive validation before deployment")

        # Determine overall risk level
        if any(rf.severity == "critical" for rf in risk_factors):
            overall_risk = "critical"
        elif any(rf.severity == "high" for rf in risk_factors):
            overall_risk = "high"
        elif any(rf.severity == "medium" for rf in risk_factors):
            overall_risk = "medium"
        else:
            overall_risk = "low"

        # Determine if review is required and if can proceed
        requires_review = overall_risk in ["high", "critical"] or plan.requires_review
        can_proceed = overall_risk not in ["critical"]

        assessment = RiskAssessment(
            risk_level=overall_risk,
            risk_factors=risk_factors,
            requires_review=requires_review,
            can_proceed=can_proceed,
            recommendations=recommendations,
            timestamp=int(time.time()),
        )

        self.risk_history.append(assessment)
        return assessment

    def check_core_logic_modification(self, modifications: List[CodeModification]) -> Optional[str]:
        """
        Check if modifications affect core logic modules

        Args:
            modifications: List of code modifications

        Returns:
            String describing affected core modules, or None if no core modules affected
        """
        affected_core_modules = []

        for mod in modifications:
            # Check if file path contains any core module
            for core_module in self.config.core_modules:
                if core_module in mod.file_path:
                    affected_core_modules.append(core_module)

        if affected_core_modules:
            return ", ".join(set(affected_core_modules))
        return None

    def evaluate_backtest_performance(
        self, current_metrics: PeriodMetrics, new_metrics: PeriodMetrics
    ) -> PerformanceComparison:
        """
        Evaluate backtest performance changes

        Args:
            current_metrics: Current strategy metrics
            new_metrics: New strategy metrics after modification

        Returns:
            Performance comparison result
        """
        # Compare key metrics
        metrics_comparison = {
            "sharpe_ratio": (current_metrics.sharpe_ratio, new_metrics.sharpe_ratio),
            "win_rate": (current_metrics.win_rate, new_metrics.win_rate),
            "profit_factor": (current_metrics.profit_factor, new_metrics.profit_factor),
            "max_drawdown": (current_metrics.max_drawdown, new_metrics.max_drawdown),
        }

        # Calculate overall improvement
        sharpe_improved = new_metrics.sharpe_ratio > current_metrics.sharpe_ratio
        win_rate_improved = new_metrics.win_rate > current_metrics.win_rate
        profit_factor_improved = new_metrics.profit_factor > current_metrics.profit_factor
        drawdown_improved = new_metrics.max_drawdown < current_metrics.max_drawdown

        improved = (
            sum([sharpe_improved, win_rate_improved, profit_factor_improved, drawdown_improved])
            >= 2
        )

        # Check for significant decline in Sharpe ratio
        if current_metrics.sharpe_ratio > 0:
            sharpe_decline = (
                current_metrics.sharpe_ratio - new_metrics.sharpe_ratio
            ) / current_metrics.sharpe_ratio
        else:
            sharpe_decline = 0.0

        significant_decline = sharpe_decline > self.config.performance_decline_threshold

        # Generate analysis
        if improved:
            analysis = "Performance improved across multiple metrics"
        elif significant_decline:
            analysis = f"Significant performance decline detected: Sharpe ratio decreased by {sharpe_decline*100:.1f}%"
        else:
            analysis = "Mixed performance changes, no clear improvement or decline"

        return PerformanceComparison(
            improved=improved,
            metrics_comparison=metrics_comparison,
            significant_decline=significant_decline,
            decline_percentage=sharpe_decline,
            analysis=analysis,
        )

    def detect_overfitting(
        self, in_sample_metrics: PeriodMetrics, out_sample_metrics: PeriodMetrics
    ) -> OverfittingAnalysis:
        """
        Detect overfitting by comparing in-sample and out-of-sample performance

        Args:
            in_sample_metrics: In-sample performance metrics
            out_sample_metrics: Out-of-sample performance metrics

        Returns:
            Overfitting analysis result
        """
        in_sample_sharpe = in_sample_metrics.sharpe_ratio
        out_sample_sharpe = out_sample_metrics.sharpe_ratio

        # Calculate performance gap
        if in_sample_sharpe > 0:
            performance_gap = (in_sample_sharpe - out_sample_sharpe) / in_sample_sharpe
        else:
            performance_gap = 0.0

        # Detect overfitting
        is_overfitted = performance_gap > self.config.overfitting_threshold

        # Calculate confidence based on sample sizes and consistency
        confidence = min(1.0, abs(performance_gap) / self.config.overfitting_threshold)

        # Generate recommendations
        recommendations = []
        if is_overfitted:
            recommendations.append("Strategy shows signs of overfitting")
            recommendations.append("Consider simplifying the strategy")
            recommendations.append("Increase out-of-sample testing period")
            recommendations.append("Review parameter optimization process")
        else:
            recommendations.append("No significant overfitting detected")
            recommendations.append("Continue monitoring out-of-sample performance")

        return OverfittingAnalysis(
            is_overfitted=is_overfitted,
            in_sample_sharpe=in_sample_sharpe,
            out_sample_sharpe=out_sample_sharpe,
            performance_gap=performance_gap,
            confidence=confidence,
            recommendations=recommendations,
        )

    def check_iteration_frequency(
        self, recent_modifications: List[ModificationRecord]
    ) -> FrequencyCheck:
        """
        Check if iteration frequency is within limits

        Args:
            recent_modifications: List of recent modification records

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

        # Count modifications today
        modifications_today = sum(1 for mod in recent_modifications if mod.timestamp >= today_start)

        # Check if within limit
        within_limit = modifications_today < self.config.max_iterations_per_day

        # Calculate next allowed time (tomorrow at 00:00)
        tomorrow_start = (
            int(datetime.now().replace(hour=0, minute=0, second=0, microsecond=0).timestamp())
            + 86400
        )

        return FrequencyCheck(
            within_limit=within_limit,
            modifications_today=modifications_today,
            max_allowed=self.config.max_iterations_per_day,
            next_allowed_time=tomorrow_start if not within_limit else int(time.time()),
        )

    def monitor_live_performance(
        self, current_pnl: float, threshold: Optional[float] = None
    ) -> LivePerformanceCheck:
        """
        Monitor live trading performance

        Args:
            current_pnl: Current profit/loss percentage (e.g., -0.05 for -5%)
            threshold: Optional custom threshold (uses config if not provided)

        Returns:
            Live performance check result
        """
        if threshold is None:
            threshold = self.config.live_loss_threshold

        # Check if within threshold
        within_threshold = current_pnl > -threshold

        # Determine if rollback is needed
        should_rollback = not within_threshold

        # Generate reason
        if within_threshold:
            reason = f"Live performance within acceptable range: {current_pnl*100:.2f}%"
        else:
            reason = (
                f"Live loss {abs(current_pnl)*100:.2f}% exceeds threshold "
                f"{threshold*100:.2f}%, rollback recommended"
            )

        return LivePerformanceCheck(
            within_threshold=within_threshold,
            current_pnl_percent=current_pnl * 100,
            threshold_percent=threshold * 100,
            should_rollback=should_rollback,
            reason=reason,
        )
