use gloo::net::http;
use thiserror::Error;
use time::{format_description::well_known::Rfc3339, OffsetDateTime};

const GITHUB_API: &str = "https://api.github.com";

pub async fn github_fetch_last_updated(
    owner: &str,
    repo: &str,
    path: &str,
) -> Result<OffsetDateTime, GithubError> {
    let commit: Vec<serde_json::Value> = http::Request::get(&format!(
        "{GITHUB_API}/repos/{owner}/{repo}/commits?path={path}&page=1&per_page=1"
    ))
    .send()
    .await?
    .json()
    .await?;

    commit[0]
        .get("commit")
        .and_then(|commit| commit.get("committer"))
        .and_then(|committer| committer.get("date"))
        .map(|date| {
            OffsetDateTime::parse(
                date.as_str().ok_or(GithubError::MissingCommitDateError)?,
                &Rfc3339,
            )
            .map_err(|err| err.into())
        })
        .unwrap_or(Err(GithubError::MissingCommitDateError))
}

pub async fn github_fetch_file(
    owner: &str,
    repo: &str,
    path: &str,
) -> Result<Vec<u8>, GithubError> {
    http::Request::get(&format!(
        "{GITHUB_API}/repos/{owner}/{repo}/contents/{path}"
    ))
    .header("Accept", "application/vnd.github.raw+json")
    .send()
    .await?
    .binary()
    .await
    .map_err(|err| err.into())
}

#[derive(Debug, Error)]
pub enum GithubError {
    #[error("no file returned from the given GitHub API")]
    NoSuchFile,
    #[error("failed to fetch from GitHub API")]
    RequestError(#[from] gloo::net::Error),
    #[error("file fetched from the GitHub API does not have a commit date")]
    MissingCommitDateError,
    #[error("date/time format reported for GitHub commit is invalid")]
    InvalidDateTimeError(#[from] time::error::Parse),
}