pub mod agent;
pub mod browser;
pub mod compat;
pub mod config;
pub mod llm;
pub mod pipe;
pub mod runtime;
pub mod security;
pub mod service;

pub use browser::ws_probe::{parse_probe_args, run_probe_script, ProbeError, ProbeOutcome};

use std::path::PathBuf;
use std::sync::Arc;
use std::time::Duration;

use agent::{handle_browser_message_with_context, AgentRuntimeContext};
use pipe::{perform_handshake, BrowserPipeTool, PipeError, StdioTransport, Transport};
use security::MacPolicy;

fn default_rules_path_from_executable(executable_path: PathBuf) -> PathBuf {
    executable_path
        .parent()
        .map(|dir| dir.join("resources").join("rules.json"))
        .unwrap_or_else(|| PathBuf::from("resources").join("rules.json"))
}

fn default_rules_path() -> PathBuf {
    std::env::current_exe()
        .map(default_rules_path_from_executable)
        .unwrap_or_else(|_| PathBuf::from("resources").join("rules.json"))
}

pub fn run() -> Result<(), PipeError> {
    let runtime_context = AgentRuntimeContext::from_process_args(std::env::args_os())?;
    let transport = Arc::new(StdioTransport::new(std::io::stdin(), std::io::stdout()));
    let handshake = perform_handshake(transport.as_ref(), Duration::from_secs(5))?;
    let mac_policy = MacPolicy::load_from_path(default_rules_path())?;
    let browser_tool = BrowserPipeTool::new(transport.clone(), mac_policy, handshake.session_key)
        .with_response_timeout(Duration::from_secs(30));

    eprintln!("sgclaw ready: agent_id={}", handshake.agent_id);

    loop {
        match transport.recv_timeout(Duration::from_secs(3600)) {
            Ok(message) => {
                handle_browser_message_with_context(
                    transport.as_ref(),
                    &browser_tool,
                    &runtime_context,
                    message,
                )?;
            }
            Err(PipeError::Timeout) => continue,
            Err(PipeError::PipeClosed) => return Ok(()),
            Err(err) => return Err(err),
        }
    }
}

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

    #[test]
    fn default_rules_path_uses_executable_directory_instead_of_cwd() {
        let executable_path = PathBuf::from("/tmp/out/KylinRelease/sgclaw");

        let resolved = default_rules_path_from_executable(executable_path);

        assert_eq!(
            resolved,
            PathBuf::from("/tmp/out/KylinRelease/resources/rules.json")
        );
    }
}
