"""Base signal interface."""

from abc import ABC, abstractmethod
from enum import Enum
from typing import Any

from pydantic import BaseModel


class SignalCategory(str, Enum):
    """Signal category types."""

    PRICE = "price"
    SOCIAL = "social"
    ONCHAIN = "onchain"


class RiskLevel(str, Enum):
    """Risk level classification."""

    LOW = "low"
    MODERATE = "moderate"
    HIGH = "high"
    EXTREME = "extreme"

    @classmethod
    def from_score(cls, score: float) -> "RiskLevel":
        """Get risk level from a score (0-100)."""
        if score <= 30:
            return cls.LOW
        elif score <= 60:
            return cls.MODERATE
        elif score <= 80:
            return cls.HIGH
        else:
            return cls.EXTREME


class SignalIndicator(BaseModel):
    """Individual signal indicator result."""

    name: str
    value: float
    score: float  # 0-100 contribution to FOMO score
    description: str
    raw_value: Any = None


class SignalResult(BaseModel):
    """Result from signal analysis."""

    category: SignalCategory
    score: float  # 0-100
    weight: float  # Weight in final FOMO score (0-1)
    indicators: list[SignalIndicator]
    risk_level: RiskLevel
    summary: str
    available: bool = True  # Whether data was available for analysis

    @property
    def weighted_score(self) -> float:
        """Get the weighted score contribution."""
        return self.score * self.weight


class Signal(ABC):
    """Abstract base class for signal analyzers."""

    category: SignalCategory
    weight: float = 0.33  # Default equal weight

    def __init__(self):
        """Initialize signal analyzer."""
        self.indicators: list[SignalIndicator] = []

    @abstractmethod
    async def analyze(self, symbol: str, data: dict[str, Any]) -> SignalResult:
        """Analyze signals for a given symbol and return results."""
        pass

    def _calculate_score(self, indicators: list[SignalIndicator]) -> float:
        """Calculate overall score from indicators."""
        if not indicators:
            return 0.0
        return sum(i.score for i in indicators) / len(indicators)

    def _create_result(
        self,
        indicators: list[SignalIndicator],
        summary: str,
        available: bool = True,
    ) -> SignalResult:
        """Create a signal result from indicators."""
        score = self._calculate_score(indicators) if available else 0.0
        return SignalResult(
            category=self.category,
            score=score,
            weight=self.weight,
            indicators=indicators,
            risk_level=RiskLevel.from_score(score),
            summary=summary,
            available=available,
        )

    @staticmethod
    def normalize_score(value: float, min_val: float, max_val: float) -> float:
        """Normalize a value to a 0-100 score."""
        if max_val == min_val:
            return 50.0
        normalized = (value - min_val) / (max_val - min_val) * 100
        return max(0.0, min(100.0, normalized))
