/// REST API conversion helpers
///
/// Converts OpenCode REST API responses to Dirigent protocol types
use crate::{
    Message, MessageMetadata, MessagePart, MessageRole, MessageStatus, Session, SessionMetadata,
};
use chrono::{DateTime, TimeZone, Utc};
use opencode_client::types as oc;

/// Convert OpenCode Session to Dirigent Session
pub fn convert_session(oc_session: oc::Session) -> Session {
    Session {
        id: oc_session.id,
        title: oc_session.title,
        created_at: timestamp_to_datetime(oc_session.time.created),
        updated_at: timestamp_to_datetime(oc_session.time.updated),
        metadata: SessionMetadata {
            project_path: oc_session.directory,
            model: None,          // Not available in session info
            total_messages: 0,    // Would need to be calculated separately
            system_message: None, // Will be set from first assistant message
            current_mode_id: None,
            _meta: None,
            project_id: None,
        },
        cwd: None, // OpenCode REST doesn't expose cwd separately from project_path
        models: None, // OpenCode doesn't provide ACP model state
        modes: None,         // OpenCode doesn't provide ACP mode state
        config_options: None,
        acp_client_id: None, // OpenCode doesn't have ACP client ID
    }
}

/// Convert OpenCode Message to Dirigent Message
pub fn convert_message(oc_msg: oc::Message) -> Message {
    let (id, session_id, role, created_at, status, metadata) = match oc_msg {
        oc::Message::User(u) => (
            u.id,
            u.session_id,
            MessageRole::User,
            timestamp_to_datetime(u.time.created),
            MessageStatus::Completed,
            None, // User messages don't have metadata
        ),
        oc::Message::Assistant(a) => {
            let status = if let Some(err) = a.error {
                MessageStatus::Failed {
                    error: format_message_error(&err),
                }
            } else if a.time.completed.is_some() {
                MessageStatus::Completed
            } else {
                MessageStatus::Streaming
            };

            // Extract metadata from assistant message
            let metadata = Some(MessageMetadata {
                cost: Some(a.cost),
                tokens_input: Some(a.tokens.input),
                tokens_output: Some(a.tokens.output),
                response_time_ms: None,
                latency_ms: None,
                model: a.model_id.clone(),
                other: None,
            });

            (
                a.id,
                a.session_id,
                MessageRole::Assistant,
                timestamp_to_datetime(a.time.created),
                status,
                metadata,
            )
        }
    };

    Message {
        id,
        session_id,
        role,
        created_at,
        content: vec![], // Parts are separate
        status,
        metadata,
    }
}

/// Convert OpenCode MessageWithParts to Dirigent Message with parts
pub fn convert_message_with_parts(oc_msg: oc::MessageWithParts) -> Message {
    let mut message = convert_message(oc_msg.info);

    // Convert parts
    message.content = oc_msg
        .parts
        .into_iter()
        .filter_map(|part| convert_part(part))
        .collect();

    message
}

/// Convert OpenCode Part to Dirigent MessagePart
fn convert_part(oc_part: oc::Part) -> Option<MessagePart> {
    match oc_part {
        oc::Part::Text(t) => Some(MessagePart::Text { text: t.text }),
        oc::Part::Reasoning(r) => Some(MessagePart::Thinking { text: r.text }),
        oc::Part::Tool(t) => {
            let (input, output) = match t.state {
                oc::ToolState::Pending => (serde_json::Value::Null, None),
                oc::ToolState::Running { input, .. } => (input, None),
                oc::ToolState::Completed { input, output, .. } => {
                    (input, Some(serde_json::Value::String(output)))
                }
                oc::ToolState::Error { input, error, .. } => {
                    (input, Some(serde_json::json!({ "error": error })))
                }
            };

            Some(MessagePart::Tool {
                tool: t.tool,
                tool_call_id: None,
                input,
                output,
            })
        }
        oc::Part::File(f) => Some(MessagePart::File {
            path: f.filename.unwrap_or_else(|| f.url.clone()),
            content: f.url,
        }),
        // Skip unsupported part types
        _ => None,
    }
}

/// Convert Unix timestamp (milliseconds) to DateTime<Utc>
fn timestamp_to_datetime(timestamp: u64) -> DateTime<Utc> {
    Utc.timestamp_millis_opt(timestamp as i64)
        .single()
        .unwrap_or_else(|| Utc::now())
}

/// Format a message error into a user-friendly string
fn format_message_error(error: &oc::MessageError) -> String {
    match error {
        oc::MessageError::ProviderAuthError { data } => {
            format!(
                "Authentication error for {}: {}",
                data.provider_id, data.message
            )
        }
        oc::MessageError::UnknownError { data } => {
            format!("Unknown error: {}", data.message)
        }
        oc::MessageError::MessageOutputLengthError => "Message output length exceeded".to_string(),
        oc::MessageError::MessageAbortedError { data } => {
            format!("Message aborted: {}", data.message)
        }
        oc::MessageError::ApiError { data } => {
            if let Some(status) = data.status_code {
                format!("API error ({}): {}", status, data.message)
            } else {
                format!("API error: {}", data.message)
            }
        }
    }
}
