use http::StatusCode;

/// Represents all of the error states of this crate.
#[derive(thiserror::Error, Debug, miette::Diagnostic)]
pub enum Error {
    #[error("No {rel:?} endpoints were found at {url:?}")]
    NoEndpointsFound { rel: String, url: String },

    #[error("The Webmention endpoint returned a unexpected status code of {0:?})")]
    WebmentionUnsupportedStatusCode(u16),

    #[error("No Webmention endpoint was provided.")]
    NoWebmentionEndpointProvided,

    #[error("The scopes {0:?} were missing in the provided list of scopes.")]
    MissingScope(crate::standards::indieauth::Scopes),

    #[error("IndieAuth error: {0}")]
    IndieAuth(#[from] crate::standards::indieauth::Error),

    #[error("Failed to build a CSS selector for {0}")]
    SelectorCompileFailure(String),

    #[error(transparent)]
    Io(#[from] std::io::Error),

    #[cfg(feature = "reqwest")]
    #[error(transparent)]
    Reqwest(#[from] Box<reqwest::Error>),

    #[cfg(feature = "reqwest")]
    #[error(transparent)]
    ReqwestMethod(#[from] http::method::InvalidMethod),

    #[cfg(feature = "reqwest")]
    #[error(transparent)]
    ReqwestMiddleware(#[from] reqwest_middleware::Error),

    #[error(transparent)]
    JSON(#[from] serde_json::Error),

    #[error(transparent)]
    FromUTF8(#[from] std::string::FromUtf8Error),

    #[error(transparent)]
    Other(#[from] Box<dyn std::error::Error + Send + Sync>),

    #[error(transparent)]
    Url(#[from] url::ParseError),

    #[error(transparent)]
    Qs(#[from] serde_qs::Error),

    #[error(transparent)]
    Microformats(#[from] crate::mf2::Error),

    #[error("The value {0} is not recognized as a valid 'order'.")]
    InvalidOrder(String),

    #[error("The value {0} is not recognized as a valid status for posts.")]
    InvalidPostStatus(String),

    #[error("The value {0} is not recognized as a known visibility of posts.")]
    InvalidVisibility(String),

    #[error("The value {0} is not a valid MIME type.")]
    InvalidMimeType(String),

    #[error("The value {0} is not a valid ISO-8601 duration.")]
    InvalidDuration(String),

    #[error("No representative h-card could be found at {0}")]
    NoRepresentativeHCardFound(url::Url),

    #[error(transparent)]
    Micropub(#[from] crate::standards::micropub::Error),

    #[error(transparent)]
    Http(#[from] ::http::Error),

    #[error(transparent)]
    HeaderValue(#[from] ::http::header::InvalidHeaderValue),

    #[error("Failed to parse the content {0} as a Microformats2 document.")]
    InvalidDocumentType(String),

    #[error("Got {0} when trying to read a remote resource.")]
    RemoteServerFailure(StatusCode),

    #[error("The content type provided was {0}")]
    ResponseNotJson(String),

    #[error("No incoming Webmention was found at {url:?}")]
    WebmentionNotFound { url: url::Url },

    #[error("The incoming Webmention at {url:?} was deleted.")]
    WebmentionDeleted { url: url::Url },

    #[error("The incoming Webmention at {url:?} could not be authenticated.")]
    WebmentionUnauthorized { url: url::Url },

    #[error(
        "The incoming Webmention at {url:?} provided a unrecognized content type of {content_type:?}."
    )]
    WebmentionUnsupportedContentType { content_type: String, url: url::Url },

    #[error("The source and target of the Webmention at {url:?} are the same.")]
    WebmentionSourceAndTargetAreSame { url: url::Url },
}

impl From<reqwest::Error> for Error {
    fn from(err: reqwest::Error) -> Self {
        Error::Reqwest(Box::new(err))
    }
}

impl PartialEq for Error {
    fn eq(&self, other: &Self) -> bool {
        std::mem::discriminant(self) == std::mem::discriminant(other)
    }
}
