use std::fs;
use std::path::{Path, PathBuf};
use std::time::{SystemTime, UNIX_EPOCH};

use sgclaw::compat::scene_platform::registry::load_scene_registry;
use sgclaw::generated_scene::analyzer::{analyze_scene_source, analyze_scene_source_with_hint, SceneKind, ToolKind};
use sgclaw::generated_scene::generator::{generate_scene_package, GenerateSceneRequest};

#[test]
fn analyzer_classifies_supported_report_collection_source() {
    let analysis = analyze_scene_source(Path::new(
        "tests/fixtures/generated_scene/report_collection",
    ))
    .unwrap();

    assert_eq!(analysis.scene_kind, SceneKind::ReportCollection);
    assert_eq!(analysis.tool_kind, ToolKind::BrowserScript);
    assert_eq!(
        analysis.bootstrap.target_url.as_deref(),
        Some("http://20.76.57.61:18080/gsllys/tqLinelossStatis/tqQualifyRateMonitor")
    );
    assert_eq!(
        analysis.bootstrap.expected_domain.as_deref(),
        Some("20.76.57.61")
    );
    assert_eq!(
        analysis.collection_entry_script.as_deref(),
        Some("js/report.js")
    );
}

#[test]
fn generator_writes_registration_ready_package_with_scene_toml() {
    let output_root = temp_workspace("sgclaw-scene-generator");

    generate_scene_package(GenerateSceneRequest {
        source_dir: PathBuf::from("tests/fixtures/generated_scene/report_collection"),
        scene_id: "sample-report-scene".to_string(),
        scene_name: "示例报表场景".to_string(),
        scene_kind: None,
        target_url: None,
        output_root: output_root.clone(),
        lessons_path: PathBuf::from("docs/superpowers/references/tq-lineloss-lessons-learned.toml"),
    })
    .unwrap();

    let skill_root = output_root.join("skills/sample-report-scene");
    assert!(skill_root.join("SKILL.toml").exists());
    assert!(skill_root.join("SKILL.md").exists());
    assert!(skill_root.join("scene.toml").exists());
    assert!(skill_root
        .join("scripts/collect_sample_report_scene.js")
        .exists());
    assert!(skill_root
        .join("scripts/collect_sample_report_scene.test.js")
        .exists());
    assert!(skill_root.join("references/generation-lessons.md").exists());
    assert!(skill_root.join("references/org-dictionary.json").exists());
    let generated_script =
        fs::read_to_string(skill_root.join("scripts/collect_sample_report_scene.js")).unwrap();
    assert!(generated_script.contains("return buildBrowserEntrypointResult(args);"));
    let generated_manifest = fs::read_to_string(skill_root.join("scene.toml")).unwrap();
    assert!(generated_manifest.contains("resolver = \"dictionary_entity\""));
    assert!(generated_manifest.contains("dictionary_ref = \"references/org-dictionary.json\""));
    assert!(generated_manifest.contains("required = true"));

    let registry = load_scene_registry(&output_root.join("skills")).unwrap();
    let entry = registry
        .iter()
        .find(|entry| entry.manifest.scene.id == "sample-report-scene")
        .expect("generated package should be registration-ready");
    assert_eq!(entry.manifest.scene.kind, "browser_script");
    assert_eq!(entry.manifest.scene.category, "report_collection");
    assert_eq!(entry.manifest.scene.tool, "collect_sample_report_scene");
    assert_eq!(entry.manifest.bootstrap.expected_domain, "20.76.57.61");
}

#[test]
fn analyzer_defaults_to_report_collection_when_no_scene_kind_meta() {
    // non_report fixture has no scene-kind meta tag - should default to ReportCollection
    let analysis =
        analyze_scene_source(Path::new("tests/fixtures/generated_scene/non_report")).unwrap();

    // With the new delegation, it defaults to ReportCollection instead of rejecting
    assert_eq!(analysis.scene_kind, SceneKind::ReportCollection);
    assert_eq!(analysis.tool_kind, ToolKind::BrowserScript);
}

fn temp_workspace(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
}

#[test]
fn analyzer_accepts_missing_meta_with_scene_kind_hint() {
    // non_report fixture has no scene-kind meta tag
    let analysis = analyze_scene_source_with_hint(
        Path::new("tests/fixtures/generated_scene/non_report"),
        Some(SceneKind::ReportCollection),
    )
    .unwrap();

    // should succeed, using hint parameter as type
    assert_eq!(analysis.scene_kind, SceneKind::ReportCollection);
}

#[test]
fn analyzer_uses_hint_when_meta_missing() {
    let analysis = analyze_scene_source_with_hint(
        Path::new("tests/fixtures/generated_scene/non_report"),
        Some(SceneKind::Monitoring),
    )
    .unwrap();

    assert_eq!(analysis.scene_kind, SceneKind::Monitoring);
}

#[test]
fn analyzer_uses_meta_when_present_and_no_hint() {
    // report_collection fixture has correct meta tag
    let analysis = analyze_scene_source_with_hint(
        Path::new("tests/fixtures/generated_scene/report_collection"),
        None,
    )
    .unwrap();

    assert_eq!(analysis.scene_kind, SceneKind::ReportCollection);
}

#[test]
fn analyzer_hint_overrides_meta() {
    // user choice takes priority over meta tag
    let analysis = analyze_scene_source_with_hint(
        Path::new("tests/fixtures/generated_scene/report_collection"),
        Some(SceneKind::Monitoring),
    )
    .unwrap();

    assert_eq!(analysis.scene_kind, SceneKind::Monitoring);
}

#[test]
fn generator_emits_monitoring_template() {
    let output_root = temp_workspace("sgclaw-monitoring-generator");

    generate_scene_package(GenerateSceneRequest {
        source_dir: PathBuf::from("tests/fixtures/generated_scene/monitoring"),
        scene_id: "sample-monitor-scene".to_string(),
        scene_name: "示例监测场景".to_string(),
        scene_kind: Some(SceneKind::Monitoring),
        target_url: None,
        output_root: output_root.clone(),
        lessons_path: PathBuf::from("docs/superpowers/references/tq-lineloss-lessons-learned.toml"),
    })
    .unwrap();

    let skill_root = output_root.join("skills/sample-monitor-scene");
    assert!(skill_root.join("SKILL.toml").exists());
    assert!(skill_root.join("scene.toml").exists());

    let generated_manifest = fs::read_to_string(skill_root.join("scene.toml")).unwrap();
    assert!(generated_manifest.contains("category = \"monitoring\""));
    // 监测类不应该有 org/period resolver
    assert!(!generated_manifest.contains("resolver = \"dictionary_entity\""));
}

#[test]
fn analyzer_extracts_domain_from_external_script() {
    // external_script fixture has no expected_domain meta tag,
    // but has an external script URL that should be auto-extracted
    let analysis = analyze_scene_source(Path::new(
        "tests/fixtures/generated_scene/external_script",
    ))
    .unwrap();

    assert_eq!(analysis.scene_kind, SceneKind::ReportCollection);
    // Should auto-extract "25.215.213.128:18080" from script src
    assert_eq!(
        analysis.bootstrap.expected_domain.as_deref(),
        Some("25.215.213.128:18080")
    );
}
