from abc import ABC, abstractmethod
from typing import Optional
import time
from altrus.core.fault.state_machine import FaultSeverity



class FaultDetector(ABC):
    """
    Base class for all fault detectors.
    """

    @abstractmethod
    def check(self) -> bool:
        pass

    @abstractmethod
    def severity(self) -> FaultSeverity:
        pass

    def module_id(self) -> Optional[str]:
        """Optional module ID associated with this detector"""
        return None


class HeartbeatDetector(FaultDetector):
    def __init__(self, target_module_id: Optional[str] = None, timeout_seconds: int = 5, severity=FaultSeverity.HARD):
        self._module_id = target_module_id
        self.timeout_seconds = timeout_seconds
        self._severity = severity
        self.last_heartbeat = time.time()

    def heartbeat(self):
        self.last_heartbeat = time.time()

    def check(self) -> bool:
        return (time.time() - self.last_heartbeat) > self.timeout_seconds

    def severity(self) -> FaultSeverity:
        return self._severity

    def module_id(self) -> Optional[str]:
        return self._module_id


class ExecutionTimeoutDetector(FaultDetector):
    """
    Detects if a module exceeds its execution time threshold for an intent.
    Matches the paper rule: ExecutionTime(M) > Threshold
    """
    def __init__(self, target_module_id: str, threshold: float = 5.0, severity=FaultSeverity.HARD):
        self._module_id = target_module_id
        self.threshold = threshold
        self._severity = severity
        self.start_time: Optional[float] = None
        self.is_executing = False

    def start_execution(self):
        self.start_time = time.time()
        self.is_executing = True

    def stop_execution(self):
        self.is_executing = False
        self.start_time = None

    def check(self) -> bool:
        if not self.is_executing or self.start_time is None:
            return False
        return (time.time() - self.start_time) > self.threshold

    def severity(self) -> FaultSeverity:
        return self._severity

    def module_id(self) -> Optional[str]:
        return self._module_id
