Documentation
use std::{
    error::Error,
    fmt::{self, Display},
    future::Future,
    pin::Pin,
};

use thiserror::Error;

use crate::event::event_repeater::{AttachError, DetachError};

pub type BoxedError = Box<dyn Error + Send + Sync>;

pub type PinnedBoxedFuture<T> = Pin<Box<dyn Future<Output = T> + Send + Sync>>;
pub type PinnedBoxedFutureResult<T> = PinnedBoxedFuture<Result<T, BoxedError>>;

pub type LifetimedPinnedBoxedFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + Sync + 'a>>;
pub type LifetimedPinnedBoxedFutureResult<'a, T> =
    LifetimedPinnedBoxedFuture<'a, Result<T, BoxedError>>;

#[derive(Debug, Clone)]
pub enum Status {
    Started,
    Stopped,
    Starting,
    Stopping,
    FailedToStart(String),
    FailedToStop(String),
    RuntimeError(String),
}

impl Display for Status {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Status::Started => write!(f, "Started"),
            Status::Stopped => write!(f, "Stopped"),
            Status::Starting => write!(f, "Starting"),
            Status::Stopping => write!(f, "Stopping"),
            Status::FailedToStart(error) => write!(f, "Failed to start: {}", error),
            Status::FailedToStop(error) => write!(f, "Failed to stop: {}", error),
            Status::RuntimeError(error) => write!(f, "Runtime error: {}", error),
        }
    }
}

impl PartialEq for Status {
    fn eq(&self, other: &Self) -> bool {
        matches!(
            (self, other),
            (Status::Started, Status::Started)
                | (Status::Stopped, Status::Stopped)
                | (Status::Starting, Status::Starting)
                | (Status::Stopping, Status::Stopping)
                | (Status::FailedToStart(_), Status::FailedToStart(_))
                | (Status::FailedToStop(_), Status::FailedToStop(_))
                | (Status::RuntimeError(_), Status::RuntimeError(_))
        )
    }
}

impl Eq for Status {}

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
pub enum OverallStatus {
    Healthy,
    Unhealthy,
}

impl Display for OverallStatus {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            OverallStatus::Healthy => write!(f, "Healthy"),
            OverallStatus::Unhealthy => write!(f, "Unhealthy"),
        }
    }
}

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
pub enum Priority {
    Essential,
    Optional,
}

impl Display for Priority {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Priority::Essential => write!(f, "Essential"),
            Priority::Optional => write!(f, "Optional"),
        }
    }
}

#[derive(Debug, Error)]
pub enum StartupError {
    #[error("Service {0} is not managed by this Service Manager")]
    ServiceNotManaged(String),

    #[error("Service {0} is not stopped")]
    ServiceNotStopped(String),

    #[error("Service {0} already has a background task running")]
    BackgroundTaskAlreadyRunning(String),

    #[error(
        "Failed to attach Service Manager's status_change EventRepeater to {0}'s status_change Event: {1}"
    )]
    StatusAttachmentFailed(String, AttachError),

    #[error("Service {0} failed to start")]
    FailedToStartService(String),
}

#[derive(Debug, Error)]
pub enum ShutdownError {
    #[error("Service {0} is not managed by this Service Manager")]
    ServiceNotManaged(String),

    #[error("Service {0} is not started")]
    ServiceNotStarted(String),

    #[error("Service {0} failed to stop")]
    FailedToStopService(String),

    #[error(
        "Failed to detach Service Manager's status_change EventRepeater from {0}'s status_change Event: {1}"
    )]
    StatusDetachmentFailed(String, DetachError),
}