use crate::error::AppError;
use crate::models::agent::Agent;
use crate::models::live_agent::{LiveMessage, LiveSession};
use crate::models::module::{ProjectModule, MODULE_AI_LIVE_CHAT};
use crate::models::project::Project;
use crate::services::agent_runtime;
use crate::AppState;
use serde::Serialize;
use tauri::{Emitter, State};

#[derive(Debug, Clone, Serialize)]
pub struct LiveAgentExchange {
    pub user_message: LiveMessage,
    pub agent_message: LiveMessage,
}

#[derive(Debug, Clone, Serialize)]
pub struct LiveAgentStreamChunkPayload {
    pub project_id: String,
    pub session_id: String,
    pub chunk: String,
}

#[derive(Debug, Clone, Serialize)]
pub struct LiveAgentStreamStatusPayload {
    pub project_id: String,
    pub session_id: String,
    pub error: Option<String>,
}

fn build_live_prompt(project: &Project, history: &[LiveMessage], user_message: &str) -> String {
    let mut prompt = format!(
        "Tu es un agent assistant pour un projet logiciel.\\n\\n## Projet\\n- Nom: {}\\n- Repo: {}\\n- Branche de base: {}\\n\\n## Consignes\\n- Réponds de manière actionnable et concise.\\n- Si tu proposes du code ou des commandes, explique brièvement pourquoi.\\n- Réponds en français.\\n\\n## Historique récent\\n",
        project.name, project.path, project.base_branch
    );

    for message in history {
        let role = if message.sender == "agent" {
            "Agent"
        } else if message.sender == "system" {
            "System"
        } else {
            "Utilisateur"
        };

        prompt.push_str(&format!("- {}: {}\\n", role, message.content));
    }

    prompt.push_str("\\n## Nouveau message\\n");
    prompt.push_str(user_message);

    prompt
}

#[tauri::command]
pub fn create_live_session(
    state: State<'_, AppState>,
    project_id: String,
    agent_id: String,
    title: String,
) -> Result<LiveSession, AppError> {
    let db = state
        .db
        .lock()
        .map_err(|e| AppError::from(format!("Database lock failed: {}", e)))?;

    let enabled = ProjectModule::is_enabled(&db, &project_id, MODULE_AI_LIVE_CHAT)?;
    if !enabled {
        return Err(AppError::from(
            "Le module de live chat est désactivé pour ce projet".to_string(),
        ));
    }

    Project::get_by_id(&db, &project_id)?;
    Agent::get_by_id(&db, &agent_id)?;

    let normalized_title = if title.trim().is_empty() {
        "Nouvelle session live".to_string()
    } else {
        title.trim().to_string()
    };

    let session = LiveSession::create(&db, &project_id, &agent_id, &normalized_title)?;
    Ok(session)
}

#[tauri::command]
pub fn list_live_sessions(
    state: State<'_, AppState>,
    project_id: String,
) -> Result<Vec<LiveSession>, AppError> {
    let db = state
        .db
        .lock()
        .map_err(|e| AppError::from(format!("Database lock failed: {}", e)))?;

    let sessions = LiveSession::list_by_project(&db, &project_id)?;
    Ok(sessions)
}

#[tauri::command]
pub fn list_live_messages(
    state: State<'_, AppState>,
    session_id: String,
) -> Result<Vec<LiveMessage>, AppError> {
    let db = state
        .db
        .lock()
        .map_err(|e| AppError::from(format!("Database lock failed: {}", e)))?;

    let messages = LiveMessage::list_by_session(&db, &session_id)?;
    Ok(messages)
}

#[tauri::command]
pub fn set_live_session_archived(
    state: State<'_, AppState>,
    session_id: String,
    archived: bool,
) -> Result<LiveSession, AppError> {
    let db = state
        .db
        .lock()
        .map_err(|e| AppError::from(format!("Database lock failed: {}", e)))?;

    let session = LiveSession::get_by_id(&db, &session_id)?;
    let enabled = ProjectModule::is_enabled(&db, &session.project_id, MODULE_AI_LIVE_CHAT)?;
    if !enabled {
        return Err(AppError::from(
            "Le module de live chat est désactivé pour ce projet".to_string(),
        ));
    }

    let updated = LiveSession::set_archived(&db, &session_id, archived)?;
    Ok(updated)
}

#[tauri::command]
pub async fn send_live_message(
    state: State<'_, AppState>,
    app_handle: tauri::AppHandle,
    session_id: String,
    message: String,
) -> Result<LiveAgentExchange, AppError> {
    let content = message.trim();
    if content.is_empty() {
        return Err(AppError::from("Le message est vide".to_string()));
    }

    let (session, project, agent, history, user_message) = {
        let db = state
            .db
            .lock()
            .map_err(|e| AppError::from(format!("Database lock failed: {}", e)))?;

        let session = LiveSession::get_by_id(&db, &session_id)?;
        let enabled = ProjectModule::is_enabled(&db, &session.project_id, MODULE_AI_LIVE_CHAT)?;
        if !enabled {
            return Err(AppError::from(
                "Le module de live chat est désactivé pour ce projet".to_string(),
            ));
        }
        if session.status == "archived" {
            return Err(AppError::from(
                "Cette discussion live est archivée et ne peut plus recevoir de message."
                    .to_string(),
            ));
        }

        let project = Project::get_by_id(&db, &session.project_id)?;
        let agent = Agent::get_by_id(&db, &session.agent_id)?;
        let history = LiveMessage::list_recent_by_session(&db, &session_id, 16)?;
        let user_message = LiveMessage::insert(&db, &session_id, "user", content)?;

        (session, project, agent, history, user_message)
    };

    let _ = app_handle.emit(
        "live-agent-message",
        serde_json::json!({
            "project_id": &session.project_id,
            "session_id": &session.id,
            "message": &user_message,
        }),
    );

    let prompt = build_live_prompt(&project, &history, content);
    let args = agent.tool.to_non_interactive_args();
    let placeholder_agent_message = {
        let db = state
            .db
            .lock()
            .map_err(|e| AppError::from(format!("Database lock failed: {}", e)))?;
        LiveMessage::insert(&db, &session_id, "agent", "")?
    };

    let _ = app_handle.emit(
        "live-agent-stream-started",
        LiveAgentStreamStatusPayload {
            project_id: session.project_id.clone(),
            session_id: session.id.clone(),
            error: None,
        },
    );

    let _ = app_handle.emit(
        "live-agent-message",
        serde_json::json!({
            "project_id": &session.project_id,
            "session_id": &session.id,
            "message": &placeholder_agent_message,
        }),
    );

    let stream_project_id = session.project_id.clone();
    let stream_session_id = session.id.clone();
    let response = agent_runtime::run_agent_command_streaming(
        agent.tool.to_command(),
        &args,
        &prompt,
        &project.path,
        600,
        |chunk| {
            app_handle
                .emit(
                    "live-agent-stream-chunk",
                    LiveAgentStreamChunkPayload {
                        project_id: stream_project_id.clone(),
                        session_id: stream_session_id.clone(),
                        chunk: chunk.to_string(),
                    },
                )
                .map_err(|e| e.to_string())
        },
    )
    .await;

    let response = match response {
        Ok(response) => response,
        Err(error) => {
            let db = state
                .db
                .lock()
                .map_err(|e| AppError::from(format!("Database lock failed: {}", e)))?;
            LiveMessage::delete(&db, &placeholder_agent_message.id)?;

            let _ = app_handle.emit(
                "live-agent-stream-error",
                LiveAgentStreamStatusPayload {
                    project_id: session.project_id.clone(),
                    session_id: session.id.clone(),
                    error: Some(error.clone()),
                },
            );

            return Err(AppError::from(error));
        }
    };

    let agent_message = {
        let db = state
            .db
            .lock()
            .map_err(|e| AppError::from(format!("Database lock failed: {}", e)))?;
        LiveMessage::update_content(&db, &placeholder_agent_message.id, &response)?;
        LiveMessage {
            content: response.clone(),
            ..placeholder_agent_message.clone()
        }
    };

    let _ = app_handle.emit(
        "live-agent-message",
        serde_json::json!({
            "project_id": &session.project_id,
            "session_id": &session.id,
            "message": &agent_message,
        }),
    );

    let _ = app_handle.emit(
        "live-agent-stream-finished",
        LiveAgentStreamStatusPayload {
            project_id: session.project_id.clone(),
            session_id: session.id.clone(),
            error: None,
        },
    );

    Ok(LiveAgentExchange {
        user_message,
        agent_message,
    })
}
