use std::{fmt, fs};

use model::{Container, Status};
use reqwest::{
    header::{self, HeaderMap},
    Client, ClientBuilder, StatusCode,
};

const KEY_PATH: &str = "/home/jade/Docs/traveldings-key";
const USER_AGENT: &str = "s10e/traveldings";
const TRAEWELLING_API_URL: &str = "https://traewelling.de/api/v1";

pub struct TraewellingClient {
    client: Client,
}

impl TraewellingClient {
    pub fn new() -> anyhow::Result<Self> {
        let mut headers = HeaderMap::new();
        let token = fs::read_to_string(KEY_PATH)?;
        let key = header::HeaderValue::from_str(&format!("Bearer {token}"))?;
        headers.insert("Authorization", key);
        headers.insert(
            header::ACCEPT,
            header::HeaderValue::from_static("application/json"),
        );
        Ok(Self {
            client: ClientBuilder::new()
                .user_agent("s10e/traveldings")
                .default_headers(headers)
                .build()?,
        })
    }

    pub async fn get_active_checkin(&self) -> Result<Status, RequestErr> {
        let res = self
            .client
            .get(Self::fmt_url("user/statuses/active"))
            .send()
            .await?;
        if res.status() != StatusCode::OK {
            return Err(RequestErr::WithStatus(res.status()));
        }

        let txt = res.text().await?;

        let res: Container<Status> = serde_json::de::from_str(&txt)?;
        Ok(res.data)
    }

    fn fmt_url(path: impl fmt::Display) -> String {
        format!("{TRAEWELLING_API_URL}/{path}")
    }
}

#[derive(thiserror::Error, Debug, PartialEq, Eq)]
pub enum RequestErr {
    #[error("Couldn't deserialize the json :(")]
    DeserializationError,
    #[error("an error related to connect happened!!")]
    RelatedToConnect,
    #[error("error haz status: {0}")]
    WithStatus(StatusCode),
    #[error("fuck if i know what went wrong :333 am silly ")]
    Other,
}

impl From<serde_json::Error> for RequestErr {
    fn from(value: serde_json::Error) -> Self {
        eprintln!("serde error: {value:?}");
        Self::DeserializationError
    }
}

impl From<reqwest::Error> for RequestErr {
    fn from(value: reqwest::Error) -> Self {
        if let Some(status) = value.status() {
            Self::WithStatus(status)
        } else if value.is_connect() {
            Self::RelatedToConnect
        } else {
            Self::Other
        }
    }
}

pub mod model;
