pub mod task_runner;

use std::sync::Arc;

use crate::browser::ws_backend::WsBrowserBackend;
use crate::browser::{BrowserBackend, PipeBrowserBackend};
use crate::pipe::{BrowserMessage, BrowserPipeTool, PipeError, Transport};

pub use task_runner::{
    run_submit_task, run_submit_task_with_browser_backend, AgentEventSink, AgentRuntimeContext,
    SubmitTaskRequest,
};

fn normalize_optional_submit_field(value: String) -> Option<String> {
    let trimmed = value.trim();
    (!trimmed.is_empty()).then(|| trimmed.to_string())
}

fn browser_backend_for_submit<T: Transport + 'static>(
    browser_tool: &BrowserPipeTool<T>,
    context: &AgentRuntimeContext,
    request: &SubmitTaskRequest,
) -> Result<Arc<dyn BrowserBackend>, PipeError> {
    if let Some(browser_ws_url) = configured_browser_ws_url(context) {
        return Ok(Arc::new(
            WsBrowserBackend::new(
                Arc::new(crate::service::browser_ws_client::ServiceWsClient::connect(
                    &browser_ws_url,
                )?),
                browser_tool.mac_policy().clone(),
                crate::service::browser_ws_client::initial_request_url_for_submit_task(request),
            )
            .with_response_timeout(browser_tool.response_timeout()),
        ));
    }

    Ok(Arc::new(PipeBrowserBackend::from_inner(browser_tool.clone())))
}

fn configured_browser_ws_url(context: &AgentRuntimeContext) -> Option<String> {
    std::env::var("SGCLAW_BROWSER_WS_URL")
        .ok()
        .filter(|value| !value.trim().is_empty())
        .or_else(|| {
            context
                .load_sgclaw_settings()
                .ok()
                .flatten()
                .and_then(|settings| settings.browser_ws_url)
                .map(|value| value.trim().to_string())
                .filter(|value| !value.is_empty())
        })
}

fn send_status_changed<T: Transport>(transport: &T, state: &str) -> Result<(), PipeError> {
    transport.send(&crate::pipe::AgentMessage::StatusChanged {
        state: state.to_string(),
    })
}

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::Connect => send_status_changed(transport, "connected"),
        BrowserMessage::Start => send_status_changed(transport, "started"),
        BrowserMessage::Stop => send_status_changed(transport, "stopped"),
        BrowserMessage::SubmitTask {
            instruction,
            conversation_id,
            messages,
            page_url,
            page_title,
        } => {
            let request = SubmitTaskRequest {
                instruction,
                conversation_id: normalize_optional_submit_field(conversation_id),
                messages,
                page_url: normalize_optional_submit_field(page_url),
                page_title: normalize_optional_submit_field(page_title),
            };
            if configured_browser_ws_url(context).is_some() {
                let browser_backend = browser_backend_for_submit(browser_tool, context, &request)?;
                run_submit_task_with_browser_backend(
                    transport,
                    transport,
                    browser_backend,
                    context,
                    request,
                )
            } else {
                run_submit_task(transport, transport, browser_tool, context, request)
            }
        }
        BrowserMessage::Init { .. } => {
            eprintln!("ignoring duplicate init after handshake");
            Ok(())
        }
        BrowserMessage::Response { seq, .. } => {
            eprintln!("ignoring unsolicited response: seq={seq}");
            Ok(())
        }
    }
}

#[cfg(test)]
mod tests {
    use super::normalize_optional_submit_field;
    use std::fs;
    use std::path::PathBuf;

    #[test]
    fn normalize_optional_submit_field_trims_and_drops_blank_values() {
        assert_eq!(normalize_optional_submit_field("   \n\t  ".to_string()), None);
        assert_eq!(
            normalize_optional_submit_field("  https://example.com/page  ".to_string()),
            Some("https://example.com/page".to_string())
        );
    }

    #[test]
    fn agent_module_cleanup_removes_legacy_runtime_and_planner_sources() {
        let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        let agent_module = fs::read_to_string(manifest_dir.join("src/agent/mod.rs")).unwrap();
        let top_lines = agent_module
            .lines()
            .take(10)
            .map(str::trim)
            .collect::<Vec<_>>();

        assert!(!manifest_dir.join("src/agent/runtime.rs").exists());
        assert!(!manifest_dir.join("src/agent/planner.rs").exists());
        assert!(!top_lines.iter().any(|line| *line == "pub mod runtime;"));
        assert!(!top_lines.iter().any(|line| *line == "pub mod planner;"));
        assert!(top_lines.iter().any(|line| *line == "pub mod task_runner;"));
    }
}
