use anyhow::Result;

use crate::transport::{HeadOutcome, Transport};
use crate::types::{Status, TakenInfo};

pub async fn check<T: Transport>(transport: &T, name: &str) -> Result<Status> {
    let url = format!("https://github.com/{name}");
    let outcome = transport.head(&url).await?;
    match outcome {
        HeadOutcome::NotFound => Ok(Status::Free),
        HeadOutcome::Found => Ok(Status::Taken(Box::new(TakenInfo {
            description: Some("user or organization name is registered".into()),
            ..Default::default()
        }))),
        HeadOutcome::Other(code) => Ok(Status::Unknown {
            reason: format!("HTTP {code}"),
        }),
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::transport::test_support::{Canned, MockTransport};

    const URL: &str = "https://github.com/example";

    #[tokio::test]
    async fn returns_free_on_404() {
        let mock = MockTransport::new().head(URL, Canned::NotFound);
        let status = check(&mock, "example").await.unwrap();
        assert!(matches!(status, Status::Free));
    }

    #[tokio::test]
    async fn returns_taken_on_200() {
        let mock = MockTransport::new().head(URL, Canned::Found);
        let status = check(&mock, "example").await.unwrap();
        let Status::Taken(info) = status else {
            panic!("expected Taken");
        };
        assert!(info.description.unwrap().contains("registered"));
    }

    #[tokio::test]
    async fn returns_unknown_on_500() {
        let mock = MockTransport::new().head(URL, Canned::Other(500));
        let status = check(&mock, "example").await.unwrap();
        assert!(matches!(status, Status::Unknown { .. }));
    }
}
