"""Provides the basic types used for checks."""

import abc
import configparser
from collections.abc import Mapping
from datetime import datetime
from enum import Enum
from typing import Any, Self, TypeVar

from autosuspend.config import ParameterSchemaAware
from autosuspend.util import logger_by_class_instance


class ConfigurationError(RuntimeError):
    """Indicates an error in the configuration of a :class:`Check`."""


class TemporaryCheckError(RuntimeError):
    """Indicates a temporary error while performing a check.

    Such an error can be ignored for some time since it might recover
    automatically.
    """


class SevereCheckError(RuntimeError):
    """Indicates a sever check error that will probably not recover.

    There is no hope this situation recovers.
    """


class ErrorBehavior(Enum):
    """Configures how a :class:`TemporaryCheckError` is handled for a check."""

    ACTIVE = "active"
    IGNORE = "ignore"


CheckType = TypeVar("CheckType", bound="Check")


class Check(abc.ABC, ParameterSchemaAware):
    """Base class for all kinds of checks.

    Subclasses must call this class' ``__init__`` method.

    Args:
        name (str):
            Configured name of the check
    """

    @classmethod
    @abc.abstractmethod
    def create(cls: type[Self], name: str, config: configparser.SectionProxy) -> Self:
        """Create a new check instance from the provided configuration.

        Args:
            name:
                user-defined name for the check
            config:
                config parser section with the configuration for this check

        Raises:
            ConfigurationError:
                Configuration for this check is inappropriate

        """

    def __init__(self, name: str | None = None) -> None:
        if name:
            self.name = name
        else:
            self.name = self.__class__.__name__
        self.logger = logger_by_class_instance(self, name)

    def options(self) -> Mapping[str, Any]:
        """Return the configured options as a mapping.

        This is used for debugging purposes only.
        """
        return {
            k: v for k, v in self.__dict__.items() if not callable(v) and k != "logger"
        }

    def __str__(self) -> str:
        return f"{self.name}[class={self.__class__.__name__}]"


class Activity(Check):
    """Base class for activity checks.

    Subclasses must call this class' __init__ method.
    """

    def __init__(self, name: str | None = None) -> None:
        super().__init__(name)
        self.error_behavior = ErrorBehavior.ACTIVE

    @abc.abstractmethod
    def check(self) -> str | None:
        """Determine if system activity exists that prevents suspending.

        Returns:
            A string describing which condition currently prevents sleep, else ``None``.

        Raises:
            TemporaryCheckError:
                Check execution currently fails but might recover later
            SevereCheckError:
                Check executions fails severely
        """

    def __str__(self) -> str:
        return f"{self.name}[class={self.__class__.__name__}]"


class Wakeup(Check):
    """Represents a check for potential wake up points."""

    @abc.abstractmethod
    def check(self, timestamp: datetime) -> datetime | None:
        """Indicate if a wakeup has to be scheduled for this check.

        Args:
            timestamp:
                the time at which the call to the wakeup check is made

        Returns:
            a datetime describing when the system needs to be running again or
            ``None`` if no wakeup is required. Use timezone aware datetimes.

        Raises:
            TemporaryCheckError:
                Check execution currently fails but might recover later
            SevereCheckError:
                Check executions fails severely
        """
