mod common;

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

use common::MockTransport;
use sgclaw::pipe::{
    Action, AgentMessage, BrowserMessage, BrowserPipeTool, ExecutionSurfaceKind, Timing,
};
use sgclaw::security::MacPolicy;

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

#[test]
fn browser_tool_signs_and_sends_command_then_waits_for_response() {
    let transport = Arc::new(MockTransport::new(vec![BrowserMessage::Response {
        seq: 1,
        success: true,
        data: serde_json::json!({"text": "ok"}),
        aom_snapshot: vec![],
        timing: Timing {
            queue_ms: 1,
            exec_ms: 20,
        },
    }]));
    let tool = BrowserPipeTool::new(
        transport.clone(),
        test_policy(),
        vec![1, 2, 3, 4, 5, 6, 7, 8],
    )
    .with_response_timeout(Duration::from_secs(1));

    let result = tool
        .invoke(
            Action::Click,
            serde_json::json!({ "selector": "#submit" }),
            "oa.example.com",
        )
        .unwrap();
    let sent = transport.sent_messages();

    assert_eq!(result.seq, 1);
    assert_eq!(result.data, serde_json::json!({"text": "ok"}));
    assert_eq!(sent.len(), 1);
    assert!(matches!(
        &sent[0],
        AgentMessage::Command {
            seq,
            action,
            params,
            security
        } if *seq == 1
            && action == &Action::Click
            && params == &serde_json::json!({"selector": "#submit"})
            && security.expected_domain == "oa.example.com"
            && !security.hmac.is_empty()
    ));
}

#[test]
fn browser_tool_rejects_action_when_mac_policy_blocks_it() {
    let transport = Arc::new(MockTransport::new(vec![]));
    let tool = BrowserPipeTool::new(transport, test_policy(), vec![1, 2, 3, 4]);

    let err = tool
        .invoke(
            Action::GetHtml,
            serde_json::json!({ "selector": "body" }),
            "oa.example.com",
        )
        .unwrap_err();

    assert!(err.to_string().contains("action is not allowed"));
}

#[test]
fn browser_tool_exposes_privileged_surface_metadata_backed_by_mac_policy() {
    let transport = Arc::new(MockTransport::new(vec![]));
    let tool = BrowserPipeTool::new(transport, test_policy(), vec![1, 2, 3, 4]);
    let metadata = tool.surface_metadata();

    assert_eq!(metadata.kind, ExecutionSurfaceKind::PrivilegedBrowserPipe);
    assert!(metadata.privileged);
    assert!(!metadata.defines_runtime_identity);
    assert_eq!(metadata.guard, "mac_policy");
    assert_eq!(
        metadata.allowed_domains,
        vec!["oa.example.com", "erp.example.com"]
    );
    assert_eq!(
        metadata.allowed_actions,
        vec!["click", "type", "navigate", "getText"]
    );
}

#[test]
fn browser_tool_accepts_approved_local_dashboard_navigate_request() {
    let transport = Arc::new(MockTransport::new(vec![BrowserMessage::Response {
        seq: 1,
        success: true,
        data: serde_json::json!({"navigated": true}),
        aom_snapshot: vec![],
        timing: Timing {
            queue_ms: 1,
            exec_ms: 20,
        },
    }]));
    let tool = BrowserPipeTool::new(transport.clone(), test_policy(), vec![1, 2, 3, 4])
        .with_response_timeout(Duration::from_secs(1));

    let result = tool
        .invoke(
            Action::Navigate,
            serde_json::json!({
                "url": "file:///C:/tmp/zhihu-hotlist-screen.html",
                "sgclaw_local_dashboard_open": {
                    "source": "compat.workflow_executor",
                    "kind": "zhihu_hotlist_screen",
                    "output_path": "C:/tmp/zhihu-hotlist-screen.html",
                    "presentation_url": "file:///C:/tmp/zhihu-hotlist-screen.html"
                }
            }),
            "__sgclaw_local_dashboard__",
        )
        .unwrap();
    let sent = transport.sent_messages();

    assert!(result.success);
    assert!(matches!(
        &sent[0],
        AgentMessage::Command {
            action,
            params,
            security,
            ..
        } if action == &Action::Navigate
            && security.expected_domain == "__sgclaw_local_dashboard__"
            && params["url"] == serde_json::json!("file:///C:/tmp/zhihu-hotlist-screen.html")
            && params["sgclaw_local_dashboard_open"]["kind"] == serde_json::json!("zhihu_hotlist_screen")
    ));
}

#[test]
fn default_rules_allow_zhihu_navigation() {
    let rules_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("resources")
        .join("rules.json");
    let policy = MacPolicy::load_from_path(rules_path).unwrap();

    policy.validate(&Action::Navigate, "www.zhihu.com").unwrap();
}

#[test]
fn mac_policy_rejects_non_html_local_dashboard_presentation() {
    let rules_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("resources")
        .join("rules.json");
    let policy = MacPolicy::load_from_path(rules_path).unwrap();

    let err = policy
        .validate_local_dashboard_presentation(
            &Action::Navigate,
            "__sgclaw_local_dashboard__",
            "file:///C:/tmp/zhihu-hotlist-screen.txt",
            "C:/tmp/zhihu-hotlist-screen.txt",
        )
        .unwrap_err();

    assert!(err.to_string().contains("local dashboard"));
}
