mod common;

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

use common::MockTransport;
use serde_json::{json, Value};
use sgclaw::security::MacPolicy;
use sgclaw::{
    compat::browser_tool_adapter::ZeroClawBrowserTool,
    pipe::{Action, AgentMessage, BrowserMessage, BrowserPipeTool, Timing},
};
use zeroclaw::tools::Tool;

fn test_policy() -> MacPolicy {
    MacPolicy::from_json_str(
        r#"{
            "version": "1.0",
            "domains": { "allowed": ["www.baidu.com"] },
            "pipe_actions": {
                "allowed": ["click", "type", "navigate", "getText"],
                "blocked": ["eval", "executeJsInPage"]
            }
        }"#,
    )
    .unwrap()
}

fn build_adapter(messages: Vec<BrowserMessage>) -> (Arc<MockTransport>, ZeroClawBrowserTool<MockTransport>) {
    let transport = Arc::new(MockTransport::new(messages));
    let browser_tool = BrowserPipeTool::new(
        transport.clone(),
        test_policy(),
        vec![1, 2, 3, 4, 5, 6, 7, 8],
    )
    .with_response_timeout(Duration::from_secs(1));

    (transport, ZeroClawBrowserTool::new(browser_tool))
}

#[test]
fn zeroclaw_browser_tool_schema_exposes_only_supported_safe_actions() {
    let (_, tool) = build_adapter(vec![]);
    let schema = tool.parameters_schema();

    assert_eq!(tool.name(), "browser_action");
    assert_eq!(
        schema["properties"]["action"]["enum"],
        json!(["click", "type", "navigate", "getText"])
    );
    assert_eq!(schema["required"], json!(["action", "expected_domain"]));
}

#[tokio::test]
async fn zeroclaw_browser_tool_executes_supported_actions_and_returns_observation_payload() {
    let (transport, tool) = build_adapter(vec![
        BrowserMessage::Response {
            seq: 1,
            success: true,
            data: json!({ "navigated": true }),
            aom_snapshot: vec![],
            timing: Timing {
                queue_ms: 1,
                exec_ms: 11,
            },
        },
        BrowserMessage::Response {
            seq: 2,
            success: true,
            data: json!({ "typed": true }),
            aom_snapshot: vec![],
            timing: Timing {
                queue_ms: 2,
                exec_ms: 12,
            },
        },
        BrowserMessage::Response {
            seq: 3,
            success: true,
            data: json!({ "clicked": true }),
            aom_snapshot: vec![],
            timing: Timing {
                queue_ms: 3,
                exec_ms: 13,
            },
        },
        BrowserMessage::Response {
            seq: 4,
            success: true,
            data: json!({ "text": "天气" }),
            aom_snapshot: vec![json!({
                "role": "textbox",
                "name": "百度一下"
            })],
            timing: Timing {
                queue_ms: 4,
                exec_ms: 14,
            },
        },
    ]);

    let navigate = tool
        .execute(json!({
            "action": "navigate",
            "expected_domain": "www.baidu.com",
            "url": "https://www.baidu.com"
        }))
        .await
        .unwrap();
    let type_text = tool
        .execute(json!({
            "action": "type",
            "expected_domain": "www.baidu.com",
            "selector": "#kw",
            "text": "天气",
            "clear_first": true
        }))
        .await
        .unwrap();
    let click = tool
        .execute(json!({
            "action": "click",
            "expected_domain": "www.baidu.com",
            "selector": "#su"
        }))
        .await
        .unwrap();
    let get_text = tool
        .execute(json!({
            "action": "getText",
            "expected_domain": "www.baidu.com",
            "selector": "#content_left"
        }))
        .await
        .unwrap();

    let navigate_output: Value = serde_json::from_str(&navigate.output).unwrap();
    let get_text_output: Value = serde_json::from_str(&get_text.output).unwrap();
    let sent = transport.sent_messages();

    assert!(navigate.success);
    assert!(type_text.success);
    assert!(click.success);
    assert!(get_text.success);
    assert_eq!(navigate_output["data"], json!({ "navigated": true }));
    assert_eq!(get_text_output["data"], json!({ "text": "天气" }));
    assert_eq!(
        get_text_output["aom_snapshot"],
        json!([{ "role": "textbox", "name": "百度一下" }])
    );
    assert_eq!(
        get_text_output["timing"],
        json!({
            "queue_ms": 4,
            "exec_ms": 14
        })
    );
    assert!(matches!(
        &sent[0],
        AgentMessage::Command { seq, action, .. }
            if *seq == 1 && action == &Action::Navigate
    ));
    assert!(matches!(
        &sent[1],
        AgentMessage::Command { seq, action, .. }
            if *seq == 2 && action == &Action::Type
    ));
    assert!(matches!(
        &sent[2],
        AgentMessage::Command { seq, action, .. }
            if *seq == 3 && action == &Action::Click
    ));
    assert!(matches!(
        &sent[3],
        AgentMessage::Command { seq, action, .. }
            if *seq == 4 && action == &Action::GetText
    ));
}

#[tokio::test]
async fn zeroclaw_browser_tool_keeps_domain_validation_in_mac_policy() {
    let (transport, tool) = build_adapter(vec![]);

    let result = tool
        .execute(json!({
            "action": "navigate",
            "expected_domain": "www.zhihu.com",
            "url": "https://www.zhihu.com"
        }))
        .await
        .unwrap();

    assert!(!result.success);
    assert!(result.output.is_empty());
    assert_eq!(transport.sent_messages().len(), 0);
    assert!(
        result
            .error
            .as_deref()
            .unwrap()
            .contains("domain is not allowed")
    );
}
