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

use crate::browser::BrowserBackend;
use crate::compat::config_adapter::resolve_skills_dir_from_sgclaw_settings;
use crate::compat::runtime::CompatTaskContext;
use crate::config::SgClawSettings;
use crate::pipe::{
    AgentMessage, BrowserPipeTool, ConversationMessage, PipeError, Transport,
};
use crate::runtime::RuntimeEngine;

#[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(resolve_process_path(PathBuf::from(value)));
                continue;
            }

            let arg_string = arg.to_string_lossy();
            if let Some(value) = arg_string.strip_prefix("--config-path=") {
                config_path = Some(resolve_process_path(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))
    }

    pub(crate) 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 resolve_process_path(path: PathBuf) -> PathBuf {
    if path.is_absolute() {
        path
    } else {
        default_workspace_root().join(path)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn from_process_args_resolves_relative_config_path_against_current_dir() {
        let current_dir = std::env::current_dir().unwrap();
        let context = AgentRuntimeContext::from_process_args([
            OsString::from("sg_claw"),
            OsString::from("--config-path"),
            OsString::from("../tmp/sgclaw_config.json"),
        ])
        .unwrap();

        assert_eq!(
            context.config_path,
            Some(current_dir.join("../tmp/sgclaw_config.json"))
        );
        assert_eq!(context.workspace_root, current_dir.join("../tmp"));
        assert!(context.workspace_root.is_absolute());
    }
}

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct SubmitTaskRequest {
    pub instruction: String,
    pub conversation_id: Option<String>,
    pub messages: Vec<ConversationMessage>,
    pub page_url: Option<String>,
    pub page_title: Option<String>,
}

pub trait AgentEventSink: Send + Sync {
    fn send(&self, message: &AgentMessage) -> Result<(), PipeError>;
}

impl<T: Transport + ?Sized> AgentEventSink for T {
    fn send(&self, message: &AgentMessage) -> Result<(), PipeError> {
        Transport::send(self, message)
    }
}

fn resolve_submit_instruction(
    instruction: String,
    page_url: Option<&str>,
    page_title: Option<&str>,
) -> Result<(String, Option<crate::compat::deterministic_submit::DeterministicExecutionPlan>), AgentMessage> {
    let raw_instruction = instruction;
    let trimmed_instruction = raw_instruction.trim().to_string();
    if trimmed_instruction.is_empty() {
        return Err(AgentMessage::TaskComplete {
            success: false,
            summary: "请输入任务内容。".to_string(),
        });
    }

    match crate::compat::deterministic_submit::decide_deterministic_submit(
        &raw_instruction,
        page_url,
        page_title,
    ) {
        crate::compat::deterministic_submit::DeterministicSubmitDecision::NotDeterministic => {
            Ok((trimmed_instruction, None))
        }
        crate::compat::deterministic_submit::DeterministicSubmitDecision::Prompt { summary } => {
            Err(AgentMessage::TaskComplete {
                success: false,
                summary,
            })
        }
        crate::compat::deterministic_submit::DeterministicSubmitDecision::Execute(plan) => {
            Ok((plan.instruction.clone(), Some(plan)))
        }
    }
}

pub fn run_submit_task<T: Transport + 'static>(
    transport: &T,
    sink: &dyn AgentEventSink,
    browser_tool: &BrowserPipeTool<T>,
    context: &AgentRuntimeContext,
    request: SubmitTaskRequest,
) -> Result<(), PipeError> {
    let SubmitTaskRequest {
        instruction,
        conversation_id,
        messages,
        page_url,
        page_title,
    } = request;

    let task_context = CompatTaskContext {
        conversation_id,
        messages,
        page_url,
        page_title,
    };
    let (instruction, deterministic_plan) = match resolve_submit_instruction(
        instruction,
        task_context.page_url.as_deref(),
        task_context.page_title.as_deref(),
    ) {
        Ok(resolved) => resolved,
        Err(completion) => return sink.send(&completion),
    };
    let _ = sink.send(&AgentMessage::LogEntry {
        level: "info".to_string(),
        message: runtime_version_log_message(),
    });
    if !task_context.messages.is_empty() {
        let _ = sink.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 _ = sink.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 _ = sink.send(&AgentMessage::LogEntry {
                level: "info".to_string(),
                message: format!("skills dir resolved to {}", resolved_skills_dir.display()),
            });
            let _ = sink.send(&AgentMessage::LogEntry {
                level: "info".to_string(),
                message: format!(
                    "runtime profile={:?} skills_prompt_mode={:?}",
                    settings.runtime_profile, settings.skills_prompt_mode
                ),
            });
            if let Some(plan) = deterministic_plan.as_ref() {
                let _ = send_mode_log(sink, "direct_skill_primary");
                let completion =
                    match crate::compat::deterministic_submit::execute_deterministic_submit(
                        browser_tool.clone(),
                        plan,
                        &context.workspace_root,
                        &settings,
                    ) {
                        Ok(outcome) => AgentMessage::TaskComplete {
                            success: outcome.success,
                            summary: outcome.summary,
                        },
                        Err(err) => AgentMessage::TaskComplete {
                            success: false,
                            summary: err.to_string(),
                        },
                    };
                return sink.send(&completion);
            }
            if RuntimeEngine::new(settings.runtime_profile).browser_surface_enabled()
                && crate::compat::orchestration::should_use_primary_orchestration(
                    &instruction,
                    task_context.page_url.as_deref(),
                    task_context.page_title.as_deref(),
                )
            {
                let _ = send_mode_log(sink, "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 sink.send(&AgentMessage::TaskComplete {
                            success: true,
                            summary,
                        });
                    }
                    Err(err) => {
                        return sink.send(&AgentMessage::TaskComplete {
                            success: false,
                            summary: err.to_string(),
                        });
                    }
                }
            }
            if settings
                .direct_submit_skill
                .as_deref()
                .map(str::trim)
                .is_some_and(|value| !value.is_empty())
            {
                match crate::compat::direct_skill_runtime::execute_direct_submit_skill(
                    browser_tool.clone(),
                    &instruction,
                    &task_context,
                    &context.workspace_root,
                    &settings,
                ) {
                    Ok(outcome) => {
                        let _ = send_mode_log(sink, "direct_skill_primary");
                        return sink.send(&AgentMessage::TaskComplete {
                            success: outcome.success,
                            summary: outcome.summary,
                        });
                    }
                    Err(PipeError::Protocol(message))
                        if message.contains("must use skill.tool format") =>
                    {
                        return sink.send(&AgentMessage::TaskComplete {
                            success: false,
                            summary: message,
                        });
                    }
                    Err(err) => {
                        return sink.send(&AgentMessage::TaskComplete {
                            success: false,
                            summary: err.to_string(),
                        });
                    }
                }
            }
            let _ = send_mode_log(sink, "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 _ = sink.send(&AgentMessage::LogEntry {
                level: "error".to_string(),
                message: format!("failed to load DeepSeek config: {err}"),
            });
            AgentMessage::TaskComplete {
                success: false,
                summary: err.to_string(),
            }
        }
    };

    sink.send(&completion)
}

pub fn run_submit_task_with_browser_backend<T: Transport + 'static>(
    _transport: &T,
    sink: &dyn AgentEventSink,
    browser_backend: Arc<dyn BrowserBackend>,
    context: &AgentRuntimeContext,
    request: SubmitTaskRequest,
) -> Result<(), PipeError> {
    let SubmitTaskRequest {
        instruction,
        conversation_id,
        messages,
        page_url,
        page_title,
    } = request;

    let task_context = CompatTaskContext {
        conversation_id,
        messages,
        page_url,
        page_title,
    };
    let (instruction, deterministic_plan) = match resolve_submit_instruction(
        instruction,
        task_context.page_url.as_deref(),
        task_context.page_title.as_deref(),
    ) {
        Ok(resolved) => resolved,
        Err(completion) => return sink.send(&completion),
    };
    let _ = sink.send(&AgentMessage::LogEntry {
        level: "info".to_string(),
        message: runtime_version_log_message(),
    });
    if !task_context.messages.is_empty() {
        let _ = sink.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 _ = sink.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 _ = sink.send(&AgentMessage::LogEntry {
                level: "info".to_string(),
                message: format!("skills dir resolved to {}", resolved_skills_dir.display()),
            });
            let _ = sink.send(&AgentMessage::LogEntry {
                level: "info".to_string(),
                message: format!(
                    "runtime profile={:?} skills_prompt_mode={:?}",
                    settings.runtime_profile, settings.skills_prompt_mode
                ),
            });
            if let Some(plan) = deterministic_plan.as_ref() {
                let _ = send_mode_log(sink, "direct_skill_primary");
                let completion = match crate::compat::deterministic_submit::execute_deterministic_submit_with_browser_backend(
                    browser_backend.clone(),
                    plan,
                    &context.workspace_root,
                    &settings,
                ) {
                    Ok(outcome) => AgentMessage::TaskComplete {
                        success: outcome.success,
                        summary: outcome.summary,
                    },
                    Err(err) => AgentMessage::TaskComplete {
                        success: false,
                        summary: err.to_string(),
                    },
                };
                return sink.send(&completion);
            }
            if RuntimeEngine::new(settings.runtime_profile).browser_surface_enabled()
                && crate::compat::orchestration::should_use_primary_orchestration(
                    &instruction,
                    task_context.page_url.as_deref(),
                    task_context.page_title.as_deref(),
                )
            {
                let _ = send_mode_log(sink, "zeroclaw_process_message_primary");
                match crate::compat::orchestration::execute_task_with_browser_backend(
                    sink,
                    browser_backend.clone(),
                    &instruction,
                    &task_context,
                    &context.workspace_root,
                    &settings,
                ) {
                    Ok(summary) => {
                        return sink.send(&AgentMessage::TaskComplete {
                            success: true,
                            summary,
                        });
                    }
                    Err(err) => {
                        return sink.send(&AgentMessage::TaskComplete {
                            success: false,
                            summary: err.to_string(),
                        });
                    }
                }
            }
            if settings
                .direct_submit_skill
                .as_deref()
                .map(str::trim)
                .is_some_and(|value| !value.is_empty())
            {
                match crate::compat::direct_skill_runtime::execute_direct_submit_skill_with_browser_backend(
                    browser_backend.clone(),
                    &instruction,
                    &task_context,
                    &context.workspace_root,
                    &settings,
                ) {
                    Ok(outcome) => {
                        let _ = send_mode_log(sink, "direct_skill_primary");
                        return sink.send(&AgentMessage::TaskComplete {
                            success: outcome.success,
                            summary: outcome.summary,
                        });
                    }
                    Err(PipeError::Protocol(message))
                        if message.contains("must use skill.tool format") =>
                    {
                        return sink.send(&AgentMessage::TaskComplete {
                            success: false,
                            summary: message,
                        });
                    }
                    Err(err) => {
                        return sink.send(&AgentMessage::TaskComplete {
                            success: false,
                            summary: err.to_string(),
                        });
                    }
                }
            }
            let _ = send_mode_log(sink, "compat_llm_primary");
            match crate::compat::runtime::execute_task_with_browser_backend(
                sink,
                browser_backend,
                &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 _ = sink.send(&AgentMessage::LogEntry {
                level: "error".to_string(),
                message: format!("failed to load DeepSeek config: {err}"),
            });
            AgentMessage::TaskComplete {
                success: false,
                summary: err.to_string(),
            }
        }
    };

    sink.send(&completion)
}

fn send_mode_log(sink: &dyn AgentEventSink, mode: &str) -> Result<(), PipeError> {
    sink.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
    )
}
