#![allow(clippy::result_large_err)]

use reqwest::StatusCode;
use serde::{Deserialize, Serialize};

use crate::{
    action::{ActionError, Context, Pair},
    action_impl::ActionImpl,
    util::http_get_to_file,
};

/// Download a file into dependencies.
///
/// Only download if the file is missing from dependencies, or has changed
/// on the server.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct HttpGet {
    items: Vec<Pair>,
}

impl HttpGet {
    pub(crate) fn new(items: Vec<Pair>) -> Self {
        Self { items }
    }

    pub(crate) fn items(&self) -> &[Pair] {
        &self.items
    }
}

impl ActionImpl for HttpGet {
    fn execute(&self, context: &mut Context) -> Result<(), ActionError> {
        let deps = context.deps_dir();
        for pair in self.items.iter() {
            let filename = deps.join(pair.filename());
            http_get_to_file(pair.url(), &filename).map_err(HttpGetError::Util)?;
        }
        Ok(())
    }
}

/// Errors from `http_get` action.
#[derive(Debug, thiserror::Error)]
pub enum HttpGetError {
    /// Forwarded from `util` module.
    #[error(transparent)]
    Util(#[from] crate::util::UtilError),

    /// Can't format time as string.
    #[error("failed to format time stamp")]
    TimeFormat(#[source] time::error::Format),

    /// Can't build an HTTP client.
    #[error("failed to create HTTP client")]
    ClientBuild(#[source] reqwest::Error),

    /// Can't create an HTTP client.
    #[error("failed to build a reqwest client")]
    Client(#[source] reqwest::Error),

    /// Can't build an HTTP request.
    #[error("failed to build a reqwest request")]
    BuildRequest(#[source] reqwest::Error),

    /// Can't get file with GET.
    #[error("failed to GET URL {0:?}")]
    Get(String, reqwest::Error),

    /// Can't get GET response body.
    #[error("failed to get body of response from {0:?}")]
    GetBody(String, reqwest::Error),

    /// HTTP GET returned weird status code.
    #[error("failure getting file with HTTP GET: status code {0}")]
    UnwantedStatus(StatusCode),
}

impl From<HttpGetError> for ActionError {
    fn from(value: HttpGetError) -> Self {
        Self::HttpGet(value)
    }
}
