use std::path::PathBuf;
use std::sync::Arc;
use std::collections::HashMap;
use std::sync::atomic::{AtomicBool, Ordering};
use anyhow::Result;
use tokio::sync::Mutex;

#[derive(serde::Deserialize, serde::Serialize, Clone, Debug)]
pub struct UserAttachment {
    pub id: String,
    pub kind: String, // "image" | "file"
    pub name: String,
    pub mime_type: String,
    pub size: u64,
    pub data_base64: Option<String>,
}

use flock_agent::agent_setup::{AgentBuilder, AssistantOverrides};
use flock_agent::sinks::OutputSink;
use flock_core::config::settings::{CliArgs, Config};
use flock_core::db::DbManager;
use flock_core::ipc_interface::approval::{ToolApprovalManager, ToolApprovalResult};
use flock_core::ipc_interface::commands::{ApprovalScope, SessionMode};
use crate::commands::ExecutionManager;

pub struct SessionMetadata {
    pub workdir: PathBuf,
    pub assistant_id: Option<String>,
    pub extra_args: Vec<String>,
}

/// 全局 Agent 状态，目前在 Tauri 层直接管理维护，看齐 Workflow 极简架构
pub struct AgentState {
    pub metadata: HashMap<String, SessionMetadata>,
    pub approval_manager: Arc<ToolApprovalManager>,
}

use tauri::{AppHandle, State, Manager};
pub type SharedAgentState = Arc<Mutex<AgentState>>;

impl AgentState {
    pub fn new() -> Self {
        Self {
            metadata: HashMap::new(),
            approval_manager: Arc::new(ToolApprovalManager::new()),
        }
    }
}

/// 辅助函数：根据当前会话配置和环境构建 AgentEngine
async fn build_session_engine(
    db_manager: Arc<DbManager>,
    workdir: PathBuf,
    session_id: Option<String>,
    assistant_id: Option<String>,
    extra_args: Vec<String>,
    approval_manager: Arc<ToolApprovalManager>,
    protocol_emitter: Arc<dyn flock_core::ipc_interface::writer::ProtocolEmitter + Send + Sync>,
    output: Arc<dyn OutputSink + Send + Sync>,
) -> Result<(flock_agent::engine::AgentEngine, bool, bool)> {
    let mut provider = None;
    let mut api_key = None;
    let mut project_dir = None;

    let mut iter = extra_args.iter();
    while let Some(arg) = iter.next() {
        match arg.as_str() {
            "--provider" => provider = iter.next().cloned(),
            "--api-key" => api_key = iter.next().cloned(),
            "--project-dir" => project_dir = iter.next().map(PathBuf::from),
            _ => {}
        }
    }

    let cli_args = CliArgs {
        provider,
        api_key,
        base_url: None,
        model: None,
        max_tokens: None,
        max_turns: None,
        system_prompt: None,
        auto_approve: false,
        project_dir,
    };

    if std::env::current_dir().map(|d| d != workdir).unwrap_or(true) {
        let _ = std::env::set_current_dir(&workdir);
    }

    let mut config = Config::resolve_from_db(&cli_args, db_manager.clone()).await?;

    let mut has_session_model_override = false;
    if let Some(ref sid) = session_id {
        let row = sqlx::query("SELECT provider, model FROM session_metadata WHERE thread_id = ?1")
            .bind(sid)
            .fetch_optional(db_manager.pool())
            .await
            .unwrap_or(None);

        if let Some(r) = row {
            use sqlx::Row;
            let prov: String = r.get("provider");
            let model: String = r.get("model");
            if !prov.is_empty() && !model.is_empty() {
                let model_override = format!("{}:{}", prov, model);
                if let Err(e) = config.apply_assistant_model_override(&model_override).await {
                    log::error!("Failed to apply session model override: {}", e);
                } else {
                    log::info!(
                        "Session overrides model to: {} (provider: {})",
                        config.model,
                        config.provider_label
                    );
                    has_session_model_override = true;
                }
            }
        }
    }

    log::info!(
        "Resolved config: provider={}, model={}, base_url={}",
        config.provider_label,
        config.model,
        config.base_url
    );

    let sid = session_id.clone().unwrap_or_else(|| "default".to_string());
    flock_tools::init_global_emitter(&sid, protocol_emitter.clone());
    flock_tools::init_global_approval_manager(&sid, approval_manager.clone());

    let mut bootstrap = AgentBuilder::new(config.clone(), workdir.to_string_lossy(), output.clone());

    if let Some(ref asst_id) = assistant_id {
        match db_manager.get_assistant(asst_id).await {
            Ok(Some(asst)) => {
                let enabled_tools: Vec<String> = asst
                    .tools
                    .into_iter()
                    .filter(|t| !asst.disabled_tools.contains(t))
                    .collect();
                let overrides = AssistantOverrides {
                    system_prompt: if asst.system_prompt.is_empty() {
                        None
                    } else {
                        Some(asst.system_prompt)
                    },
                    model: if has_session_model_override {
                        None
                    } else if asst.model.is_empty() {
                        None
                    } else {
                        Some(asst.model)
                    },
                    allowed_tool_providers: Some(enabled_tools),
                    allowed_skill_names: Some(asst.skills),
                };
                bootstrap = bootstrap.with_assistant(overrides);
            }
            Ok(None) => {
                log::warn!("Assistant '{}' not found in DB, using default config.", asst_id);
            }
            Err(e) => {
                log::error!("Failed to load assistant '{}': {}", asst_id, e);
            }
        }
    }

    let mut raw_paths: Vec<PathBuf> = Vec::new();
    if let Ok(rows) = sqlx::query("SELECT path FROM imported_skill")
        .fetch_all(db_manager.pool())
        .await
    {
        use sqlx::Row;
        for row in rows {
            if let Ok(path_str) = row.try_get::<String, _>("path") {
                raw_paths.push(PathBuf::from(path_str));
            }
        }
    }

    let extra_dirs: Vec<String> = db_manager
        .get_config("extra_skill_dirs")
        .await
        .unwrap_or_default();
    for d in extra_dirs {
        raw_paths.push(PathBuf::from(d));
    }

    if !raw_paths.is_empty() {
        bootstrap = bootstrap.add_raw_skill_paths(raw_paths);
    }
    
    let mut is_resumed = false;
    if let Some(sid_val) = &session_id {
        if let Some(db) = &config.db_manager {
            let session_mgr = db.session_manager(config.session.max_sessions);
            if let Ok(session) = session_mgr.load(sid_val).await {
                log::info!("Resuming session: {}", sid_val);
                bootstrap = bootstrap.resume(session);
                is_resumed = true;
            }
        }
    }

    let result = bootstrap.build().await?;
    let mut engine = result.engine;

    engine.set_approval_manager(approval_manager);
    engine.set_protocol_writer(protocol_emitter.clone());

    Ok((engine, is_resumed, result.has_mcp))
}

/// 启动 Agent 引擎
pub async fn start_agent_engine(
    db_manager: Arc<DbManager>,
    state: Arc<Mutex<AgentState>>,
    workdir: PathBuf,
    session_id: Option<String>,
    assistant_id: Option<String>,
    extra_args: Vec<String>,
    protocol_emitter: Arc<dyn flock_core::ipc_interface::writer::ProtocolEmitter + Send + Sync>,
    output: Arc<dyn OutputSink + Send + Sync>,
) -> Result<()> {
    let sid = session_id.clone().unwrap_or_else(|| "default".to_string());
    let approval_manager = {
        let s = state.lock().await;
        s.approval_manager.clone()
    };

    let (mut engine, is_resumed, has_mcp) = build_session_engine(
        db_manager.clone(),
        workdir.clone(),
        session_id.clone(),
        assistant_id.clone(),
        extra_args.clone(),
        approval_manager,
        protocol_emitter.clone(),
        output.clone(),
    ).await?;

    let provider_name = engine.provider_label.clone();
    if !is_resumed {
        log::info!("Initializing new session...");
        engine.init_session(&provider_name, &workdir.to_string_lossy(), session_id.as_deref()).await?;
    }

    let sid_actual = engine.current_session_id();
    log::info!("Agent ready. Session ID: {:?}", sid_actual);

    {
        let s = state.lock().await;
        let ready_event = flock_core::ipc_interface::events::ProtocolEvent::Ready {
            version: "0.1.0".to_string(),
            session_id: sid_actual.clone(),
            capabilities: flock_core::ipc_interface::events::Capabilities {
                tool_approval: true,
                thinking: engine.compat().supports_thinking(),
                effort: engine.compat().supports_effort(),
                effort_levels: engine.compat().effort_levels().to_vec(),
                modes: vec!["default".into(), "auto_edit".into(), "yolo".into()],
                current_mode: s.approval_manager.current_mode(),
                mcp: has_mcp,
            },
        };
        let _ = protocol_emitter.emit(&ready_event);
    }

    {
        let mut s = state.lock().await;
        s.metadata.insert(
            sid,
            SessionMetadata {
                workdir,
                assistant_id,
                extra_args,
            },
        );
    }

    Ok(())
}

/// 发送消息（无状态运行，异步拉起）
pub async fn send_message_to_engine(
    state: Arc<Mutex<AgentState>>,
    execution_manager: Arc<ExecutionManager>,
    session_id: Option<String>,
    msg_id: String,
    content: String,
    attachments: Option<Vec<UserAttachment>>,
    db_manager: Arc<DbManager>,
    protocol_emitter: Arc<dyn flock_core::ipc_interface::writer::ProtocolEmitter + Send + Sync>,
    output: Arc<dyn OutputSink + Send + Sync>,
) -> Result<()> {
    let sid = session_id.unwrap_or_else(|| "default".to_string());
    log::info!("Sending message [{}] for session {}: {}", msg_id, sid, content);

    let run_content = if let Some(ref atts) = attachments {
        if !atts.is_empty() {
            serde_json::json!({
                "text": content,
                "attachments": atts,
            }).to_string()
        } else {
            content.clone()
        }
    } else {
        content.clone()
    };

    // 1. 获取会话元数据与运行状态检查
    let (workdir, assistant_id, extra_args) = {
        let s = state.lock().await;
        if execution_manager.is_task_running(&sid).await {
            anyhow::bail!("当前会话正在执行中，请等待回复完成后再发送消息。");
        }
        if let Some(m) = s.metadata.get(&sid) {
            (m.workdir.clone(), m.assistant_id.clone(), m.extra_args.clone())
        } else {
            anyhow::bail!("会话 {} 尚未启动，请先初始化", sid);
        }
    };

    let approval_manager = {
        let s = state.lock().await;
        s.approval_manager.clone()
    };

    // 3. 构建单次运行的 Engine
    let (mut engine, _, _) = build_session_engine(
        db_manager.clone(),
        workdir,
        Some(sid.clone()),
        assistant_id,
        extra_args,
        approval_manager,
        protocol_emitter.clone(),
        output,
    ).await?;

    let is_running = Arc::new(AtomicBool::new(true));
    let cancel_flag = Arc::new(AtomicBool::new(false));
    engine.set_cancel_flag(cancel_flag.clone());

    let is_running_clone = is_running.clone();
    let cancel_flag_for_guard = cancel_flag.clone();
    let cancel_flag_for_spawn = cancel_flag.clone();
    let cancel_flag_for_check = cancel_flag.clone();
    let sid_clone = sid.clone();
    let content_clone = run_content.clone();
    let msg_id_clone = msg_id.clone();

    struct SessionCleanupGuard {
        state: Arc<Mutex<AgentState>>,
        execution_manager: Arc<ExecutionManager>,
        session_id: String,
        msg_id: String,
        cancel_flag: Arc<AtomicBool>,
        /// 捕获 per-session emitter，保证 cleanup task 发出的 StreamEnd 带正确 session_id
        protocol_emitter: Arc<dyn flock_core::ipc_interface::writer::ProtocolEmitter + Send + Sync>,
    }

    impl Drop for SessionCleanupGuard {
        fn drop(&mut self) {
            let state = self.state.clone();
            let execution_manager = self.execution_manager.clone();
            let session_id = self.session_id.clone();
            let msg_id = self.msg_id.clone();
            let cancel_flag = self.cancel_flag.clone();
            let emitter = self.protocol_emitter.clone();
            tokio::spawn(async move {
                let _s = state.lock().await;
                execution_manager.unregister_task(&session_id).await;
                flock_tools::unregister_global_emitter(&session_id);
                flock_tools::unregister_global_approval_manager(&session_id);
                flock_tools::unregister_workspace_dir(&session_id);
                if cancel_flag.load(Ordering::SeqCst) {
                    let _ = emitter.emit(&flock_core::ipc_interface::events::ProtocolEvent::StreamEnd {
                        msg_id,
                        usage: None,
                    });
                }
            });
        }
    }

    let state_clone = state.clone();
    let execution_manager_clone = execution_manager.clone();
    let sid_clone2 = sid.clone();
    let msg_id_clone2 = msg_id.clone();
    let emitter_for_guard = protocol_emitter.clone();

    // 4. 异步拉起单次运行
    let join_handle = tokio::spawn(async move {
        let _guard = SessionCleanupGuard {
            state: state_clone,
            execution_manager: execution_manager_clone,
            session_id: sid_clone2,
            msg_id: msg_id_clone2,
            cancel_flag: cancel_flag_for_guard,
            protocol_emitter: emitter_for_guard,
        };

        let run_result = flock_core::CURRENT_SESSION_ID.scope(sid_clone.clone(), async {
            engine.run(&content_clone, &msg_id_clone).await
        }).await;

        if let Err(err) = run_result {
            if matches!(err, flock_agent::engine::AgentError::UserAborted) || cancel_flag_for_spawn.load(Ordering::SeqCst) {
                log::info!("Agent run for session {} aborted by user.", sid_clone);
                engine.output().emit_stream_end(&msg_id_clone, 0, 0, 0, 0, 0);
            } else {
                log::error!("Agent run error for session {}: {}", sid_clone, err);
                engine.output().emit_error(&format!("Agent 运行出错: {}", err));
            }
        }
        is_running_clone.store(false, Ordering::SeqCst);
    });

    if cancel_flag_for_check.load(Ordering::SeqCst) {
        join_handle.abort();
    } else {
        execution_manager.register_task(sid, join_handle, cancel_flag, is_running).await;
    }

    Ok(())
}

/// 批准工具调用
pub async fn approve_tool_call(
    state: Arc<Mutex<AgentState>>,
    call_id: String,
    scope: String,
    feedback: Option<String>,
) -> Result<()> {
    let scope_enum = match scope.as_str() {
        "always" => ApprovalScope::Always,
        _ => ApprovalScope::Once,
    };

    // First, try to approve using the global registry (e.g. for workflows)
    if flock_tools::approve_by_global_registry(&call_id, scope_enum.clone(), feedback.clone()) {
        return Ok(());
    }

    let s = state.lock().await;
    s.approval_manager.approve(&call_id, scope_enum, feedback);
    Ok(())
}

/// 拒绝工具调用
pub async fn deny_tool_call(
    state: Arc<Mutex<AgentState>>,
    call_id: String,
    reason: Option<String>,
) -> Result<()> {
    let denied_result = ToolApprovalResult::Denied {
        reason: reason.unwrap_or_else(|| "User denied".to_string()),
    };

    // First, try to deny using the global registry (e.g. for workflows)
    if flock_tools::deny_by_global_registry(&call_id, denied_result.clone()) {
        return Ok(());
    }

    let s = state.lock().await;
    s.approval_manager.resolve(&call_id, denied_result);
    Ok(())
}

/// 设置模式
pub async fn set_approval_mode(state: Arc<Mutex<AgentState>>, mode: String) -> Result<()> {
    let s = state.lock().await;
    let mode_enum = match mode.as_str() {
        "auto_edit" => SessionMode::AutoEdit,
        "yolo" => SessionMode::Yolo,
        _ => SessionMode::Default,
    };
    s.approval_manager.set_mode(mode_enum);
    Ok(())
}

/// 更新配置 (无状态模式下配置变化主要通过数据库持久化，此接口做日志记录)
pub async fn set_engine_config(
    _state: Arc<Mutex<AgentState>>,
    session_id: Option<String>,
    model: Option<String>,
    thinking: Option<String>,
    thinking_budget: Option<u32>,
    effort: Option<String>,
    compaction: Option<String>,
) -> Result<()> {
    log::info!(
        "set_config called for session={:?}, model={:?}, thinking={:?}, thinking_budget={:?}, effort={:?}, compaction={:?}",
        session_id, model, thinking, thinking_budget, effort, compaction
    );
    Ok(())
}

/// 启动 Agent（工作目录自动绑定工作空间路径）
#[tauri::command]
pub async fn start_agent(
    app: AppHandle,
    state: State<'_, SharedAgentState>,
    workdir: String,
    project_dir: Option<String>,
    api_key: Option<String>,
    session_id: Option<String>,
    assistant_id: Option<String>,
    extra_args: Option<Vec<String>>,
) -> Result<(), String> {
    let workdir = PathBuf::from(&workdir);
    let mut args = extra_args.unwrap_or_default();

    if let Some(ref pd) = project_dir {
        args.push("--project-dir".to_string());
        args.push(pd.clone());
    }

    if let Some(ref key) = api_key {
        args.push("--api-key".to_string());
        args.push(key.clone());
    }

    let db_manager = app.state::<Arc<flock_core::db::DbManager>>().inner().clone();
    let sid_str = session_id.clone().unwrap_or_else(|| "default".to_string());
    let emitter = Arc::new(crate::ipc::emitter::TauriProtocolEmitter::new(app.clone(), sid_str));
    let output = emitter.clone() as Arc<dyn flock_agent::sinks::OutputSink + Send + Sync>;

    start_agent_engine(
        db_manager,
        state.inner().clone(),
        workdir,
        session_id,
        assistant_id,
        args,
        emitter,
        output,
    )
    .await
    .map_err(|e| e.to_string())
}

/// 停止 Agent
#[tauri::command]
pub async fn stop_agent(
    execution_manager: State<'_, Arc<ExecutionManager>>,
    session_id: Option<String>,
) -> Result<(), String> {
    let sid = session_id.unwrap_or_else(|| "default".to_string());
    execution_manager.cancel_task(&sid).await;
    Ok(())
}

/// 发送消息给 Agent
#[tauri::command]
pub async fn send_message(
    app: AppHandle,
    state: State<'_, SharedAgentState>,
    execution_manager: State<'_, Arc<ExecutionManager>>,
    session_id: Option<String>,
    msg_id: String,
    content: String,
    attachments: Option<Vec<UserAttachment>>,
) -> Result<(), String> {
    let db_manager = app.state::<Arc<flock_core::db::DbManager>>().inner().clone();
    let sid_str = session_id.clone().unwrap_or_else(|| "default".to_string());
    let emitter = Arc::new(crate::ipc::emitter::TauriProtocolEmitter::new(app.clone(), sid_str));
    let output = emitter.clone() as Arc<dyn flock_agent::sinks::OutputSink + Send + Sync>;
    send_message_to_engine(state.inner().clone(), execution_manager.inner().clone(), session_id, msg_id, content, attachments, db_manager, emitter, output)
        .await
        .map_err(|e| e.to_string())
}

/// 更新配置
#[tauri::command]
pub async fn set_config(
    state: State<'_, SharedAgentState>,
    session_id: Option<String>,
    model: Option<String>,
    thinking: Option<String>,
    thinking_budget: Option<u32>,
    effort: Option<String>,
    compaction: Option<String>,
) -> Result<(), String> {
    set_engine_config(
        state.inner().clone(),
        session_id,
        model,
        thinking,
        thinking_budget,
        effort,
        compaction,
    )
    .await
    .map_err(|e| e.to_string())
}

/// 获取当前工作目录
#[tauri::command]
pub async fn get_workdir(
    state: State<'_, SharedAgentState>,
    session_id: Option<String>,
) -> Result<Option<String>, String> {
    let s = state.lock().await;
    let sid = session_id.unwrap_or_else(|| "default".to_string());
    Ok(s.metadata.get(&sid).map(|h| h.workdir.to_string_lossy().to_string()))
}
