//! Backend-Client für /api/magic/* — alle HTTP-Calls laufen im Rust-Core.
//! Der mgc_-Session-Token erreicht die WebView nie (analog Mac-Keychain-Modell).

use serde::Deserialize;
use serde_json::json;

#[derive(Deserialize)]
struct Envelope<T> {
    #[allow(dead_code)]
    success: bool,
    data: T,
}

#[derive(Deserialize)]
struct ErrorBody {
    message: Option<String>,
}

#[derive(Deserialize)]
pub struct PairData {
    pub token: String,
}

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MeData {
    pub nickname: Option<String>,
    pub plan: Option<String>,
}

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RegisterData {
    pub dns_token: String,
}

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MagicDevice {
    pub device_id: String,
    pub release_requested_at: Option<String>,
    pub release_available_at: Option<String>,
}

pub struct Api {
    base: String,
    client: reqwest::Client,
}

impl Api {
    pub fn new(base: &str) -> Self {
        Self {
            base: base.trim_end_matches('/').to_string(),
            client: reqwest::Client::new(),
        }
    }

    async fn parse<T: serde::de::DeserializeOwned>(
        res: reqwest::Response,
    ) -> Result<T, String> {
        let status = res.status();
        let text = res.text().await.map_err(|e| e.to_string())?;
        if !status.is_success() {
            let msg = serde_json::from_str::<ErrorBody>(&text)
                .ok()
                .and_then(|b| b.message)
                .unwrap_or_else(|| format!("HTTP {status}"));
            return Err(msg);
        }
        serde_json::from_str::<Envelope<T>>(&text)
            .map(|e| e.data)
            .map_err(|e| format!("Unerwartete Server-Antwort: {e}"))
    }

    /// POST /api/magic/pair/redeem — 6-stelliger Code → mgc_-Session-Token.
    pub async fn redeem_pair(&self, code: &str, label: &str) -> Result<PairData, String> {
        let res = self
            .client
            .post(format!("{}/api/magic/pair/redeem", self.base))
            .json(&json!({ "code": code, "label": label }))
            .send()
            .await
            .map_err(|e| format!("Keine Verbindung zum Server: {e}"))?;
        Self::parse(res).await
    }

    /// GET /api/magic/me — Hub-Header-Infos.
    pub async fn me(&self, token: &str) -> Result<MeData, String> {
        let res = self
            .client
            .get(format!("{}/api/magic/me", self.base))
            .bearer_auth(token)
            .send()
            .await
            .map_err(|e| format!("Keine Verbindung zum Server: {e}"))?;
        Self::parse(res).await
    }

    /// POST /api/magic/register — DNS-Token + AdGuard-Provisioning (idempotent).
    pub async fn register(
        &self,
        token: &str,
        device_id: &str,
        hostname: &str,
        model: &str,
        os_version: &str,
    ) -> Result<RegisterData, String> {
        let res = self
            .client
            .post(format!("{}/api/magic/register", self.base))
            .bearer_auth(token)
            .json(&json!({
                "deviceId": device_id,
                "hostname": hostname,
                "model": model,
                "osVersion": os_version,
                "platform": "windows",
            }))
            .send()
            .await
            .map_err(|e| format!("Keine Verbindung zum Server: {e}"))?;
        Self::parse(res).await
    }

    /// GET /api/magic/devices — aktive Magic-Bindings des Users.
    pub async fn devices(&self, token: &str) -> Result<Vec<MagicDevice>, String> {
        let res = self
            .client
            .get(format!("{}/api/magic/devices", self.base))
            .bearer_auth(token)
            .send()
            .await
            .map_err(|e| format!("Keine Verbindung zum Server: {e}"))?;
        Self::parse(res).await
    }

    /// POST /api/magic/devices/:id/request-release — startet 24h-Cooldown.
    pub async fn request_release(&self, token: &str, device_id: &str) -> Result<(), String> {
        let res = self
            .client
            .post(format!(
                "{}/api/magic/devices/{device_id}/request-release",
                self.base
            ))
            .bearer_auth(token)
            .send()
            .await
            .map_err(|e| format!("Keine Verbindung zum Server: {e}"))?;
        Self::parse::<serde_json::Value>(res).await.map(|_| ())
    }

    /// POST /api/magic/devices/:id/cancel-release — bricht Cooldown ab.
    pub async fn cancel_release(&self, token: &str, device_id: &str) -> Result<(), String> {
        let res = self
            .client
            .post(format!(
                "{}/api/magic/devices/{device_id}/cancel-release",
                self.base
            ))
            .bearer_auth(token)
            .send()
            .await
            .map_err(|e| format!("Keine Verbindung zum Server: {e}"))?;
        Self::parse::<serde_json::Value>(res).await.map(|_| ())
    }
}
