mod common;

use std::collections::HashMap;
use std::fs;
use std::path::PathBuf;
use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH};

use common::MockTransport;
use sgclaw::browser::{BrowserBackend, PipeBrowserBackend};
use sgclaw::compat::browser_script_skill_tool::build_browser_script_skill_tools;
use sgclaw::pipe::{Action, CommandOutput, ExecutionSurfaceKind, ExecutionSurfaceMetadata};
use sgclaw::security::MacPolicy;
use zeroclaw::skills::{Skill, SkillTool};

fn backend_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()
}

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

#[test]
fn pipe_browser_backend_keeps_privileged_pipe_surface_metadata() {
    let transport = Arc::new(MockTransport::new(vec![]));
    let backend = PipeBrowserBackend::new(transport, backend_policy(), vec![1, 2, 3, 4]);

    let metadata = backend.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 pipe_browser_backend_reports_eval_capability_from_mac_policy() {
    let transport = Arc::new(MockTransport::new(vec![]));
    let backend = PipeBrowserBackend::new(transport, eval_policy(), vec![1, 2, 3, 4]);

    assert!(backend.supports_eval());
}

#[test]
fn browser_script_tools_are_hidden_when_backend_cannot_eval() {
    let skill_root = unique_temp_dir("sgclaw-browser-backend-capability");
    let scripts_dir = skill_root.join("scripts");
    fs::create_dir_all(&scripts_dir).unwrap();
    fs::write(
        scripts_dir.join("extract_hotlist.js"),
        "return { rows: [[1, '标题', '10万热度']] };",
    )
    .unwrap();

    let skills = vec![Skill {
        name: "zhihu-hotlist".to_string(),
        description: "Zhihu hotlist helpers".to_string(),
        version: "1.0.0".to_string(),
        author: None,
        tags: vec![],
        tools: vec![SkillTool {
            name: "extract_hotlist".to_string(),
            description: "Extract structured hotlist rows".to_string(),
            kind: "browser_script".to_string(),
            command: "scripts/extract_hotlist.js".to_string(),
            args: HashMap::new(),
        }],
        prompts: vec![],
        location: Some(skill_root.join("skill.json")),
    }];
    let backend: Arc<dyn BrowserBackend> = Arc::new(FakeBrowserBackend::new(false));

    let tools = build_browser_script_skill_tools(&skills, backend).unwrap();

    assert!(tools.is_empty());
}

#[derive(Default)]
struct FakeBrowserBackend {
    supports_eval: bool,
}

impl FakeBrowserBackend {
    fn new(supports_eval: bool) -> Self {
        Self { supports_eval }
    }
}

impl BrowserBackend for FakeBrowserBackend {
    fn invoke(
        &self,
        _action: Action,
        _params: serde_json::Value,
        _expected_domain: &str,
    ) -> Result<CommandOutput, sgclaw::pipe::PipeError> {
        panic!("invoke should not be called in this capability-gating test")
    }

    fn surface_metadata(&self) -> ExecutionSurfaceMetadata {
        ExecutionSurfaceMetadata::privileged_browser_pipe("fake_backend")
    }

    fn supports_eval(&self) -> bool {
        self.supports_eval
    }
}

fn unique_temp_dir(prefix: &str) -> PathBuf {
    let nanos = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap()
        .as_nanos();
    let path = std::env::temp_dir().join(format!("{prefix}-{nanos}"));
    fs::create_dir_all(&path).unwrap();
    path
}
