//! OpenCode API Client
//!
//! HTTP client for interacting with opencode.ai API

use crate::sse::{SseClient, SseError, SseStream};
use crate::types::{MessageWithParts, Session};
use serde::Serialize;
use std::sync::Arc;

/// Logging callback type for API client events
pub type LogCallback = Arc<dyn Fn(&str, &str) + Send + Sync>;

#[derive(Clone)]
pub struct OpenCodeClient {
    base_url: String,
    client: reqwest::Client,
    log_info: Option<LogCallback>,
    log_success: Option<LogCallback>,
    log_error: Option<LogCallback>,
}

impl OpenCodeClient {
    /// Create a new OpenCode API client
    pub fn new(base_url: impl Into<String>) -> Self {
        Self {
            base_url: base_url.into(),
            client: reqwest::Client::new(),
            log_info: None,
            log_success: None,
            log_error: None,
        }
    }

    /// Set logging callbacks for the client
    pub fn with_logging(
        mut self,
        log_info: LogCallback,
        log_success: LogCallback,
        log_error: LogCallback,
    ) -> Self {
        self.log_info = Some(log_info);
        self.log_success = Some(log_success);
        self.log_error = Some(log_error);
        self
    }

    fn log_info(&self, category: &str, message: &str) {
        if let Some(logger) = &self.log_info {
            logger(category, message);
        }
    }

    fn log_success(&self, category: &str, message: &str) {
        if let Some(logger) = &self.log_success {
            logger(category, message);
        }
    }

    fn log_error(&self, category: &str, message: &str) {
        if let Some(logger) = &self.log_error {
            logger(category, message);
        }
    }

    /// List all sessions
    pub async fn list_sessions(&self) -> Result<Vec<Session>, ClientError> {
        let url = format!("{}/session", self.base_url);
        self.log_info("API", &format!("GET {}", url));

        let response = self.client.get(&url).send().await?;
        let status = response.status();

        if !status.is_success() {
            let error_body = response
                .text()
                .await
                .unwrap_or_else(|_| String::from("(no body)"));
            self.log_error("API", &format!("GET {} failed: {}", url, status));
            self.log_error("API", &format!("Response body: {}", error_body));
            return Err(ClientError::Http(status));
        }

        let text = response.text().await?;
        self.log_info("API", &format!("Response: {} bytes", text.len()));

        match serde_json::from_str::<Vec<Session>>(&text) {
            Ok(sessions) => {
                self.log_success("API", &format!("Loaded {} sessions", sessions.len()));
                Ok(sessions)
            }
            Err(e) => {
                self.log_error("API", &format!("Failed to decode sessions: {}", e));
                self.log_error("API", &format!("Response body: {}", text));
                Err(ClientError::Serialization(e))
            }
        }
    }

    /// Get a specific session by ID
    pub async fn get_session(&self, session_id: &str) -> Result<Session, ClientError> {
        let url = format!("{}/session/{}", self.base_url, session_id);
        let response = self.client.get(&url).send().await?;

        if !response.status().is_success() {
            return Err(ClientError::Http(response.status()));
        }

        let session = response.json::<Session>().await?;
        Ok(session)
    }

    /// List messages in a session
    pub async fn list_messages(
        &self,
        session_id: &str,
    ) -> Result<Vec<MessageWithParts>, ClientError> {
        let url = format!("{}/session/{}/message", self.base_url, session_id);
        self.log_info("API", &format!("GET {}", url));

        let response = self.client.get(&url).send().await?;
        let status = response.status();

        if !status.is_success() {
            let error_body = response
                .text()
                .await
                .unwrap_or_else(|_| String::from("(no body)"));
            self.log_error("API", &format!("GET {} failed: {}", url, status));
            self.log_error("API", &format!("Response body: {}", error_body));
            return Err(ClientError::Http(status));
        }

        let text = response.text().await?;
        self.log_info("API", &format!("Response: {} bytes", text.len()));

        // Try to parse as array first to get individual message values
        match serde_json::from_str::<serde_json::Value>(&text) {
            Ok(serde_json::Value::Array(message_values)) => {
                let mut messages = Vec::new();
                for (index, msg_value) in message_values.iter().enumerate() {
                    match serde_json::from_value::<MessageWithParts>(msg_value.clone()) {
                        Ok(msg) => messages.push(msg),
                        Err(e) => {
                            self.log_error(
                                "API",
                                &format!("Failed to decode message at index {}: {}", index, e),
                            );
                            self.log_error(
                                "API",
                                &format!(
                                    "Problematic message JSON: {}",
                                    serde_json::to_string_pretty(msg_value)
                                        .unwrap_or_else(|_| msg_value.to_string())
                                ),
                            );
                            return Err(ClientError::Serialization(e));
                        }
                    }
                }
                self.log_success("API", &format!("Loaded {} messages", messages.len()));
                Ok(messages)
            }
            Ok(_) => {
                self.log_error("API", "Response is not an array");
                self.log_error("API", &format!("Response body: {}", text));
                // Create a dummy error by trying to parse invalid data
                let dummy_err = serde_json::from_str::<Vec<MessageWithParts>>("null").unwrap_err();
                Err(ClientError::Serialization(dummy_err))
            }
            Err(e) => {
                self.log_error("API", &format!("Failed to parse JSON: {}", e));
                self.log_error(
                    "API",
                    &format!(
                        "Response body (first 1000 chars): {}",
                        if text.len() > 1000 {
                            &text[..1000]
                        } else {
                            &text
                        }
                    ),
                );
                Err(ClientError::Serialization(e))
            }
        }
    }

    /// Send a chat message to a session
    pub async fn send_message(
        &self,
        session_id: &str,
        text: String,
    ) -> Result<MessageWithParts, ClientError> {
        #[derive(Debug, Serialize)]
        struct ChatPart {
            #[serde(rename = "type")]
            part_type: String,
            text: String,
        }

        #[derive(Debug, Serialize)]
        struct ChatInput {
            parts: Vec<ChatPart>,
        }

        let url = format!("{}/session/{}/message", self.base_url, session_id);
        self.log_info("API", &format!("POST {} (text: {} chars)", url, text.len()));

        let input = ChatInput {
            parts: vec![ChatPart {
                part_type: "text".to_string(),
                text,
            }],
        };

        let response = self.client.post(&url).json(&input).send().await?;
        let status = response.status();

        if !status.is_success() {
            let error_body = response
                .text()
                .await
                .unwrap_or_else(|_| String::from("(no body)"));
            self.log_error("API", &format!("POST {} failed: {}", url, status));
            self.log_error("API", &format!("Response body: {}", error_body));
            return Err(ClientError::Http(status));
        }

        let response_text = response.text().await?;
        self.log_info("API", &format!("Response: {} bytes", response_text.len()));

        match serde_json::from_str::<MessageWithParts>(&response_text) {
            Ok(message) => {
                self.log_success("API", "Message sent successfully");
                Ok(message)
            }
            Err(e) => {
                self.log_error("API", &format!("Failed to decode message: {}", e));
                self.log_error("API", &format!("Response body: {}", response_text));
                Err(ClientError::Serialization(e))
            }
        }
    }

    /// Subscribe to server-sent events (SSE) for real-time updates
    ///
    /// Returns a stream of events including message updates, session changes, etc.
    /// The stream will automatically handle the SSE protocol and deserialize events.
    ///
    /// # Example
    ///
    /// ```no_run
    /// # use opencode_client::OpenCodeClient;
    /// # use futures::stream::StreamExt;
    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
    /// let client = OpenCodeClient::new("http://localhost:12225");
    /// let mut stream = client.subscribe_events()?;
    ///
    /// while let Some(event_result) = stream.next().await {
    ///     match event_result {
    ///         Ok(event) => println!("Received event: {:?}", event),
    ///         Err(e) => eprintln!("Event error: {}", e),
    ///     }
    /// }
    /// # Ok(())
    /// # }
    /// ```
    pub fn subscribe_events(&self) -> Result<SseStream, SseError> {
        self.log_info("SSE", &format!("Connecting to {}/event", self.base_url));

        let sse_client = SseClient::new(&self.base_url);
        let stream = sse_client.connect()?;

        self.log_success("SSE", "Connected to event stream");
        Ok(stream)
    }

    /// Abort an in-progress message generation in a session
    ///
    /// Sends a POST request to `/session/:id/abort` to cancel the current generation.
    ///
    /// # Returns
    ///
    /// Returns `Ok(true)` if the abort was successful, `Ok(false)` if there was nothing to abort,
    /// or an error if the request failed.
    pub async fn abort_session(&self, session_id: &str) -> Result<bool, ClientError> {
        let url = format!("{}/session/{}/abort", self.base_url, session_id);
        self.log_info("API", &format!("POST {} (abort)", url));

        let response = self.client.post(&url).send().await?;
        let status = response.status();

        if !status.is_success() {
            let error_body = response
                .text()
                .await
                .unwrap_or_else(|_| String::from("(no body)"));
            self.log_error("API", &format!("POST {} failed: {}", url, status));
            self.log_error("API", &format!("Response body: {}", error_body));
            return Err(ClientError::Http(status));
        }

        self.log_success("API", "Generation aborted successfully");
        Ok(true)
    }
}

#[derive(Debug)]
pub enum ClientError {
    Http(reqwest::StatusCode),
    Request(reqwest::Error),
    Serialization(serde_json::Error),
}

impl From<reqwest::Error> for ClientError {
    fn from(err: reqwest::Error) -> Self {
        ClientError::Request(err)
    }
}

impl From<serde_json::Error> for ClientError {
    fn from(err: serde_json::Error) -> Self {
        ClientError::Serialization(err)
    }
}

impl std::fmt::Display for ClientError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ClientError::Http(status) => write!(f, "HTTP error: {}", status),
            ClientError::Request(err) => write!(f, "Request error: {}", err),
            ClientError::Serialization(err) => write!(f, "Serialization error: {}", err),
        }
    }
}

impl std::error::Error for ClientError {}
