pub mod planner;
pub mod runtime;

use std::ffi::OsString;
use std::path::PathBuf;

use crate::compat::config_adapter::resolve_skills_dir_from_sgclaw_settings;
use crate::compat::runtime::CompatTaskContext;
use crate::config::SgClawSettings;
use crate::pipe::{
    AgentMessage, BrowserMessage, BrowserPipeTool, PipeError, Transport,
};

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AgentRuntimeContext {
    config_path: Option<PathBuf>,
    workspace_root: PathBuf,
}

impl AgentRuntimeContext {
    pub fn new(config_path: Option<PathBuf>, workspace_root: PathBuf) -> Self {
        Self {
            config_path,
            workspace_root,
        }
    }

    pub fn from_process_args<I, S>(args: I) -> Result<Self, PipeError>
    where
        I: IntoIterator<Item = S>,
        S: Into<OsString>,
    {
        let mut config_path = None;
        let mut args = args.into_iter().map(Into::into);
        let _ = args.next();

        while let Some(arg) = args.next() {
            if arg == OsString::from("--config-path") {
                let Some(value) = args.next() else {
                    return Err(PipeError::Protocol(
                        "missing value for --config-path".to_string(),
                    ));
                };
                config_path = Some(PathBuf::from(value));
                continue;
            }

            let arg_string = arg.to_string_lossy();
            if let Some(value) = arg_string.strip_prefix("--config-path=") {
                config_path = Some(PathBuf::from(value));
            }
        }

        let workspace_root = config_path
            .as_ref()
            .and_then(|path| path.parent().map(|parent| parent.to_path_buf()))
            .unwrap_or_else(default_workspace_root);

        Ok(Self::new(config_path, workspace_root))
    }

    fn load_sgclaw_settings(&self) -> Result<Option<SgClawSettings>, PipeError> {
        SgClawSettings::load(self.config_path.as_deref())
            .map_err(|err| PipeError::Protocol(err.to_string()))
    }

    fn settings_source_label(&self) -> String {
        match &self.config_path {
            Some(path) if path.exists() => path.display().to_string(),
            _ => "environment".to_string(),
        }
    }
}

impl Default for AgentRuntimeContext {
    fn default() -> Self {
        Self::new(None, default_workspace_root())
    }
}

fn default_workspace_root() -> PathBuf {
    std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."))
}

fn send_mode_log<T: Transport>(transport: &T, mode: &str) -> Result<(), PipeError> {
    transport.send(&AgentMessage::LogEntry {
        level: "mode".to_string(),
        message: mode.to_string(),
    })
}

fn missing_llm_configuration_summary() -> String {
    "未配置大语言模型。请先在 sgclaw_config.json 或环境变量中配置 apiKey、baseUrl 与 model。"
        .to_string()
}

fn runtime_version_log_message() -> String {
    format!(
        "sgclaw runtime version={} protocol={}",
        env!("CARGO_PKG_VERSION"),
        crate::pipe::protocol::PROTOCOL_VERSION
    )
}

fn execute_plan<T: Transport>(
    transport: &T,
    browser_tool: &BrowserPipeTool<T>,
    plan: &planner::TaskPlan,
) -> Result<String, PipeError> {
    for step in &plan.steps {
        transport.send(&AgentMessage::LogEntry {
            level: "info".to_string(),
            message: step.log_message.clone(),
        })?;

        let result = browser_tool.invoke(
            step.action.clone(),
            step.params.clone(),
            &step.expected_domain,
        )?;
        if !result.success {
            return Err(PipeError::Protocol(format!(
                "browser action failed: {}",
                result.data
            )));
        }
    }

    Ok(plan.summary.clone())
}

pub fn execute_task<T: Transport>(
    transport: &T,
    browser_tool: &BrowserPipeTool<T>,
    instruction: &str,
) -> Result<String, PipeError> {
    let plan = planner::plan_instruction(instruction)
        .map_err(|err| PipeError::Protocol(err.to_string()))?;
    execute_plan(transport, browser_tool, &plan)
}

pub fn handle_browser_message<T: Transport + 'static>(
    transport: &T,
    browser_tool: &BrowserPipeTool<T>,
    message: BrowserMessage,
) -> Result<(), PipeError> {
    handle_browser_message_with_context(
        transport,
        browser_tool,
        &AgentRuntimeContext::default(),
        message,
    )
}

pub fn handle_browser_message_with_context<T: Transport + 'static>(
    transport: &T,
    browser_tool: &BrowserPipeTool<T>,
    context: &AgentRuntimeContext,
    message: BrowserMessage,
) -> Result<(), PipeError> {
    match message {
        BrowserMessage::SubmitTask {
            instruction,
            conversation_id,
            messages,
            page_url,
            page_title,
        } => {
            let instruction = instruction.trim().to_string();
            if instruction.is_empty() {
                return transport.send(&AgentMessage::TaskComplete {
                    success: false,
                    summary: "请输入任务内容。".to_string(),
                });
            }

            let task_context = CompatTaskContext {
                conversation_id: (!conversation_id.trim().is_empty())
                    .then_some(conversation_id.clone()),
                messages,
                page_url: (!page_url.trim().is_empty()).then_some(page_url),
                page_title: (!page_title.trim().is_empty()).then_some(page_title),
            };
            let _ = transport.send(&AgentMessage::LogEntry {
                level: "info".to_string(),
                message: runtime_version_log_message(),
            });
            if !task_context.messages.is_empty() {
                let _ = transport.send(&AgentMessage::LogEntry {
                    level: "info".to_string(),
                    message: format!(
                        "continuing conversation with {} prior turns",
                        task_context.messages.len()
                    ),
                });
            }
            let completion = match context.load_sgclaw_settings() {
                Ok(Some(settings)) => {
                    let resolved_skills_dir =
                        resolve_skills_dir_from_sgclaw_settings(&context.workspace_root, &settings);
                    let _ = transport.send(&AgentMessage::LogEntry {
                        level: "info".to_string(),
                        message: format!(
                            "DeepSeek config loaded from {} model={} base_url={}",
                            context.settings_source_label(),
                            settings.provider_model,
                            settings.provider_base_url
                        ),
                    });
                    let _ = transport.send(&AgentMessage::LogEntry {
                        level: "info".to_string(),
                        message: format!(
                            "skills dir resolved to {}",
                            resolved_skills_dir.display()
                        ),
                    });
                    let _ = transport.send(&AgentMessage::LogEntry {
                        level: "info".to_string(),
                        message: format!(
                            "runtime profile={:?} skills_prompt_mode={:?}",
                            settings.runtime_profile,
                            settings.skills_prompt_mode
                        ),
                    });
                    if crate::compat::orchestration::should_use_primary_orchestration(
                        &instruction,
                        task_context.page_url.as_deref(),
                        task_context.page_title.as_deref(),
                    ) {
                        let _ = send_mode_log(transport, "zeroclaw_process_message_primary");
                        match crate::compat::orchestration::execute_task_with_sgclaw_settings(
                            transport,
                            browser_tool.clone(),
                            &instruction,
                            &task_context,
                            &context.workspace_root,
                            &settings,
                        ) {
                            Ok(summary) => {
                                return transport.send(&AgentMessage::TaskComplete {
                                    success: true,
                                    summary,
                                })
                            }
                            Err(err) => {
                                return transport.send(&AgentMessage::TaskComplete {
                                    success: false,
                                    summary: err.to_string(),
                                })
                            }
                        }
                    }
                    let _ = send_mode_log(transport, "compat_llm_primary");
                    match crate::compat::runtime::execute_task_with_sgclaw_settings(
                        transport,
                        browser_tool.clone(),
                        &instruction,
                        &task_context,
                        &context.workspace_root,
                        &settings,
                    ) {
                        Ok(summary) => AgentMessage::TaskComplete {
                            success: true,
                            summary,
                        },
                        Err(err) => AgentMessage::TaskComplete {
                            success: false,
                            summary: err.to_string(),
                        },
                    }
                }
                Ok(None) => AgentMessage::TaskComplete {
                    success: false,
                    summary: missing_llm_configuration_summary(),
                },
                Err(err) => {
                    let _ = transport.send(&AgentMessage::LogEntry {
                        level: "error".to_string(),
                        message: format!("failed to load DeepSeek config: {err}"),
                    });
                    AgentMessage::TaskComplete {
                        success: false,
                        summary: err.to_string(),
                    }
                }
            };
            transport.send(&completion)
        }
        BrowserMessage::Init { .. } => {
            eprintln!("ignoring duplicate init after handshake");
            Ok(())
        }
        BrowserMessage::Response { seq, .. } => {
            eprintln!("ignoring unsolicited response: seq={seq}");
            Ok(())
        }
    }
}
