use discord_rich_presence::activity::{Activity, Assets, Timestamps};
use discord_rich_presence::{DiscordIpc, DiscordIpcClient};
use parking_lot::RwLock;
use std::sync::Arc;

pub struct DiscordRpcManager {
    client: Arc<RwLock<Option<DiscordIpcClient>>>,
    session_name: Arc<RwLock<String>>,
    model: Arc<RwLock<String>>,
    started_at: Arc<RwLock<i64>>,
}

impl DiscordRpcManager {
    pub fn new() -> Self {
        Self {
            client: Arc::new(RwLock::new(None)),
            session_name: Arc::new(RwLock::new(String::new())),
            model: Arc::new(RwLock::new(String::new())),
            started_at: Arc::new(RwLock::new(0)),
        }
    }

    pub fn init(&self, initial_session_name: String, initial_model: String, started_at: i64) -> Result<(), String> {
        tracing::debug!("Attempting to initialize Discord RPC...");
        tracing::debug!("Application ID: 1391117878182281316");
        tracing::debug!("Initial session: '{}', model: '{}', timestamp: {}",
            initial_session_name, initial_model, started_at);

        let mut client = DiscordIpcClient::new("1391117878182281316")
            .map_err(|e| {
                let error_msg = format!("Failed to create Discord RPC client: {} (is Discord running?)", e);
                tracing::error!("{}", error_msg);
                error_msg
            })?;

        tracing::debug!("DiscordIpcClient created successfully");

        client
            .connect()
            .map_err(|e| {
                let error_msg = format!("Failed to connect to Discord RPC: {} (ensure Discord is running)", e);
                tracing::error!("{}", error_msg);
                error_msg
            })?;

        tracing::debug!("Connected to Discord IPC socket");

        // Set initial activity immediately after connecting
        tracing::debug!("Building initial activity...");
        let state_text = format!("Model: {}", initial_model);
        let assets = Assets::new()
            .large_image("hikari")
            .large_text("Hikari - Claude Code Assistant");

        tracing::debug!("Assets created - large_image: 'hikari', large_text: 'Hikari - Claude Code Assistant'");

        let timestamps = Timestamps::new()
            .start(started_at);

        tracing::debug!("Timestamps created - start: {}", started_at);

        let activity = Activity::new()
            .details(initial_session_name.as_str())
            .state(state_text.as_str())
            .assets(assets)
            .timestamps(timestamps);

        tracing::debug!("Activity created - details: '{}', state: '{}'",
            initial_session_name, state_text);

        tracing::debug!("Attempting to set initial activity...");
        client
            .set_activity(activity)
            .map_err(|e| {
                let error_msg = format!("Failed to set initial Discord RPC activity: {}", e);
                tracing::error!("{}", error_msg);
                error_msg
            })?;

        tracing::debug!("Initial activity set successfully!");

        // Store the client and initial state
        *self.client.write() = Some(client);
        *self.session_name.write() = initial_session_name.clone();
        *self.model.write() = initial_model.clone();
        *self.started_at.write() = started_at;

        tracing::info!("Discord RPC connected successfully with initial activity: session='{}', model='{}'",
            initial_session_name, initial_model);
        Ok(())
    }

    pub fn update(
        &self,
        session_name: String,
        model: String,
        started_at: i64,
    ) -> Result<(), String> {
        tracing::debug!("update() called with session='{}', model='{}', timestamp={}",
            session_name, model, started_at);

        *self.session_name.write() = session_name.clone();
        *self.model.write() = model.clone();
        *self.started_at.write() = started_at;

        tracing::debug!("State variables updated");

        let mut client_guard = self.client.write();
        let client = client_guard
            .as_mut()
            .ok_or_else(|| {
                let error_msg = "Discord RPC client not initialized".to_string();
                tracing::error!("{}", error_msg);
                error_msg
            })?;

        tracing::debug!("Client lock acquired");

        let state_text = format!("Model: {}", model);
        let assets = Assets::new()
            .large_image("hikari")
            .large_text("Hikari - Claude Code Assistant");

        tracing::debug!("Assets created - large_image: 'hikari', large_text: 'Hikari - Claude Code Assistant'");

        let timestamps = Timestamps::new()
            .start(started_at);

        tracing::debug!("Timestamps created - start: {}", started_at);

        let activity = Activity::new()
            .details(session_name.as_str())
            .state(state_text.as_str())
            .assets(assets)
            .timestamps(timestamps);

        tracing::debug!("Activity created - details: '{}', state: '{}'",
            session_name, state_text);

        tracing::debug!("Attempting to set activity...");
        client
            .set_activity(activity)
            .map_err(|e| {
                let error_msg = format!("Failed to update Discord RPC: {}", e);
                tracing::error!("{}", error_msg);
                error_msg
            })?;

        tracing::info!("Updated Discord RPC: session='{}', model='{}'", session_name, model);
        Ok(())
    }

    pub fn stop(&self) -> Result<(), String> {
        tracing::debug!("stop() called");

        let mut client_guard = self.client.write();
        if let Some(mut client) = client_guard.take() {
            tracing::debug!("Client found, attempting to close...");
            client
                .close()
                .map_err(|e| {
                    let error_msg = format!("Failed to close Discord RPC: {}", e);
                    tracing::error!("{}", error_msg);
                    error_msg
                })?;
            tracing::info!("Discord RPC stopped successfully");
        } else {
            tracing::debug!("No client to stop (already stopped or never initialized)");
        }
        Ok(())
    }
}

impl Default for DiscordRpcManager {
    fn default() -> Self {
        Self::new()
    }
}
