pub mod discovery;
pub mod error;
pub mod jetstream;
pub mod types;

pub use error::{EgregoreError, Result};
use reqwest::Client as HttpClient;
use serde::Deserialize;
use types::*;

pub const DEFAULT_BACKEND_URL: &str = "https://egregore.inanna-c38.workers.dev";

#[derive(Debug, Deserialize)]
pub struct AuthStartResponse {
    #[serde(rename = "authUrl")]
    pub auth_url: String,
    #[serde(rename = "sessionId")]
    pub session_id: String,
}

#[derive(Debug, Deserialize)]
pub struct AuthPollResponse {
    pub status: String,
    pub token: Option<String>,
    pub did: Option<String>,
    pub handle: Option<String>,
    pub error: Option<String>,
}

pub struct EgregoreClient {
    backend_url: String,
    token: Option<String>,
    did: String,
}

impl EgregoreClient {
    pub fn new(backend_url: &str, token: Option<String>, did: String) -> Self {
        Self {
            backend_url: backend_url.to_string(),
            token,
            did,
        }
    }

    pub fn daemon_did(&self) -> &str {
        &self.did
    }

    pub fn get_token(&self) -> Option<&str> {
        self.token.as_deref()
    }

    /// Start the server-side OAuth flow. Returns (auth_url, session_id).
    pub async fn auth_start(&self, handle: &str) -> Result<AuthStartResponse> {
        let url = format!("{}/auth/start", self.backend_url);
        let resp = HttpClient::new()
            .post(&url)
            .json(&serde_json::json!({ "handle": handle }))
            .send()
            .await?;

        if !resp.status().is_success() {
            return Err(EgregoreError::Backend {
                status: resp.status().as_u16(),
                body: resp.text().await?,
            });
        }

        Ok(resp.json::<AuthStartResponse>().await?)
    }

    /// Poll the backend for OAuth completion.
    pub async fn auth_poll(&self, session_id: &str) -> Result<AuthPollResponse> {
        let url = format!(
            "{}/auth/poll?session={}",
            self.backend_url,
            urlencoding::encode(session_id)
        );
        let resp = HttpClient::new().get(&url).send().await?;

        if !resp.status().is_success() {
            return Err(EgregoreError::Backend {
                status: resp.status().as_u16(),
                body: resp.text().await?,
            });
        }

        Ok(resp.json::<AuthPollResponse>().await?)
    }

    /// Register via devBypass (development only).
    pub async fn register_dev(&mut self) -> Result<String> {
        let url = format!("{}/api/daemons", self.backend_url);
        let resp = HttpClient::new()
            .post(&url)
            .json(&serde_json::json!({ "devBypass": true }))
            .send()
            .await?;

        if !resp.status().is_success() {
            return Err(EgregoreError::Backend {
                status: resp.status().as_u16(),
                body: resp.text().await?,
            });
        }

        let result = resp.json::<serde_json::Value>().await?;
        let token = result["token"]
            .as_str()
            .ok_or_else(|| EgregoreError::Other("No token in response".to_string()))?
            .to_string();
        self.token = Some(token.clone());
        if let Some(did) = result["did"].as_str() {
            self.did = did.to_string();
        }
        Ok(token)
    }

    fn auth_header(&self) -> Result<String> {
        self.token
            .as_ref()
            .map(|t| format!("Bearer {}", t))
            .ok_or(EgregoreError::NotAuthenticated)
    }

    async fn authed_request(
        &self,
        url: &str,
        method: reqwest::Method,
        body: Option<serde_json::Value>,
    ) -> Result<reqwest::Response> {
        let auth = self.auth_header()?;
        let mut rb = HttpClient::new()
            .request(method, url)
            .header("Authorization", &auth);
        if let Some(ref b) = body {
            rb = rb.json(b);
        }
        Ok(rb.send().await?)
    }

    pub async fn create_socket(&self, name: &str, description: &str) -> Result<()> {
        let url = format!("{}/api/sockets", self.backend_url);
        let resp = self
            .authed_request(
                &url,
                reqwest::Method::POST,
                Some(serde_json::json!({ "name": name, "description": description })),
            )
            .await?;
        if !resp.status().is_success() {
            return Err(EgregoreError::Backend {
                status: resp.status().as_u16(),
                body: resp.text().await?,
            });
        }
        Ok(())
    }

    pub async fn list_sockets(&self) -> Result<Vec<String>> {
        let url = format!("{}/api/sockets", self.backend_url);
        let resp = HttpClient::new().get(&url).send().await?;
        if !resp.status().is_success() {
            return Err(EgregoreError::Backend {
                status: resp.status().as_u16(),
                body: resp.text().await?,
            });
        }
        Ok(resp.json::<Vec<String>>().await?)
    }

    pub async fn bind(&self, socket: &str) -> Result<()> {
        let url = format!(
            "{}/api/sockets/{}/bind",
            self.backend_url,
            urlencoding::encode(socket)
        );
        let resp = self
            .authed_request(
                &url,
                reqwest::Method::POST,
                Some(serde_json::json!({ "did": self.daemon_did() })),
            )
            .await?;
        if !resp.status().is_success() {
            return Err(EgregoreError::Backend {
                status: resp.status().as_u16(),
                body: resp.text().await?,
            });
        }
        Ok(())
    }

    pub async fn unbind(&self, socket: &str) -> Result<()> {
        let url = format!(
            "{}/api/sockets/{}/unbind",
            self.backend_url,
            urlencoding::encode(socket)
        );
        let resp = self
            .authed_request(
                &url,
                reqwest::Method::POST,
                Some(serde_json::json!({ "did": self.daemon_did() })),
            )
            .await?;
        if !resp.status().is_success() {
            return Err(EgregoreError::Backend {
                status: resp.status().as_u16(),
                body: resp.text().await?,
            });
        }
        Ok(())
    }

    /// Inscribe a signal. Backend generates the AT URI.
    pub async fn inscribe(
        &self,
        socket: &str,
        content: SignalContent,
        priority: Option<i32>,
    ) -> Result<String> {
        let signal = Signal {
            signal_type: match &content {
                SignalContent::Interrupt { .. } => SignalType::Interrupt,
                SignalContent::Query { .. } => SignalType::Query,
                SignalContent::Patch { .. } => SignalType::Patch,
                SignalContent::Fork { .. } => SignalType::Fork,
            },
            socket: socket.to_string(),
            content,
            created_at: chrono::Utc::now(),
            author_did: None,
            uri: None,
            priority,
        };

        let url = format!("{}/api/signals", self.backend_url);
        let resp = self
            .authed_request(
                &url,
                reqwest::Method::POST,
                Some(serde_json::to_value(&signal)?),
            )
            .await?;
        if !resp.status().is_success() {
            return Err(EgregoreError::Backend {
                status: resp.status().as_u16(),
                body: resp.text().await?,
            });
        }

        let result = resp.json::<serde_json::Value>().await?;
        Ok(result["uri"].as_str().unwrap_or("(no uri)").to_string())
    }

    /// Ack a signal. Backend generates the AT URI.
    pub async fn ack(&self, socket: &str, signal_uri: &str, body: &str) -> Result<String> {
        let ack = Ack {
            subject: signal_uri.to_string(),
            body: body.to_string(),
            created_at: chrono::Utc::now(),
            author_did: None,
            uri: None,
            socket: socket.to_string(),
        };

        let url = format!("{}/api/acks", self.backend_url);
        let resp = self
            .authed_request(
                &url,
                reqwest::Method::POST,
                Some(serde_json::to_value(&ack)?),
            )
            .await?;
        if !resp.status().is_success() {
            return Err(EgregoreError::Backend {
                status: resp.status().as_u16(),
                body: resp.text().await?,
            });
        }

        let result = resp.json::<serde_json::Value>().await?;
        Ok(result["uri"].as_str().unwrap_or("(no uri)").to_string())
    }

    pub async fn scry(&self, filter: ScryFilter) -> Result<Vec<Signal>> {
        let mut url = format!(
            "{}/api/sockets/{}/feed?limit={}",
            self.backend_url,
            urlencoding::encode(&filter.socket),
            filter.limit
        );
        if let Some(since) = filter.since {
            url.push_str(&format!(
                "&since={}",
                urlencoding::encode(&since.to_rfc3339())
            ));
        }
        let resp = HttpClient::new().get(&url).send().await?;
        if !resp.status().is_success() {
            return Err(EgregoreError::Backend {
                status: resp.status().as_u16(),
                body: resp.text().await?,
            });
        }
        Ok(resp.json::<Vec<Signal>>().await?)
    }

    pub async fn hook_context(&self) -> Result<HookContextBundle> {
        let url = format!(
            "{}/api/daemons/{}/hook-context",
            self.backend_url,
            urlencoding::encode(self.daemon_did())
        );
        let resp = self
            .authed_request(&url, reqwest::Method::GET, None)
            .await?;
        if !resp.status().is_success() {
            return Err(EgregoreError::Backend {
                status: resp.status().as_u16(),
                body: resp.text().await?,
            });
        }
        Ok(resp.json::<HookContextBundle>().await?)
    }

    pub async fn commune(&self, target_did: &str) -> Result<()> {
        let url = format!(
            "{}/api/daemons/{}/commune",
            self.backend_url,
            urlencoding::encode(self.daemon_did())
        );
        let resp = self
            .authed_request(
                &url,
                reqwest::Method::POST,
                Some(serde_json::json!({ "targetDid": target_did })),
            )
            .await?;
        if !resp.status().is_success() {
            return Err(EgregoreError::Backend {
                status: resp.status().as_u16(),
                body: resp.text().await?,
            });
        }
        Ok(())
    }

    pub async fn uncommune(&self, target_did: &str) -> Result<()> {
        let url = format!(
            "{}/api/daemons/{}/uncommune",
            self.backend_url,
            urlencoding::encode(self.daemon_did())
        );
        let resp = self
            .authed_request(
                &url,
                reqwest::Method::POST,
                Some(serde_json::json!({ "targetDid": target_did })),
            )
            .await?;
        if !resp.status().is_success() {
            return Err(EgregoreError::Backend {
                status: resp.status().as_u16(),
                body: resp.text().await?,
            });
        }
        Ok(())
    }
}
