use pretty_assertions::assert_eq;

use std::collections::BTreeMap;

use super::HookEventsToml;
use super::HookHandlerConfig;
use super::HooksFile;
use super::HooksToml;
use super::ManagedHooksRequirementsToml;
use super::MatcherGroup;

#[test]
fn hooks_file_deserializes_existing_json_shape() {
    let parsed: HooksFile = serde_json::from_str(
        r#"{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "^Bash$",
        "hooks": [
          {
            "type": "command",
            "command": "python3 /tmp/pre.py",
            "timeout": 10,
            "statusMessage": "checking"
          }
        ]
      }
    ]
  }
}"#,
    )
    .expect("hooks.json should deserialize");

    assert_eq!(
        parsed,
        HooksFile {
            hooks: HookEventsToml {
                pre_tool_use: vec![MatcherGroup {
                    matcher: Some("^Bash$".to_string()),
                    hooks: vec![HookHandlerConfig::Command {
                        command: "python3 /tmp/pre.py".to_string(),
                        command_windows: None,
                        timeout_sec: Some(10),
                        r#async: false,
                        status_message: Some("checking".to_string()),
                    }],
                }],
                ..Default::default()
            },
        }
    );
}

#[test]
fn hooks_file_rejects_events_outside_hooks_object() {
    let error = serde_json::from_str::<HooksFile>(
        r#"{
  "SessionStart": [
    {
      "hooks": [
        {
          "type": "command",
          "command": "python3 /tmp/session_start.py"
        }
      ]
    }
  ]
}"#,
    )
    .expect_err("root-level hook events should be rejected");

    assert!(
        error.to_string().contains("unknown field `SessionStart`"),
        "unexpected parse error: {error}"
    );
}

#[test]
fn hook_events_deserialize_from_toml_arrays_of_tables() {
    let parsed: HookEventsToml = toml::from_str(
        r#"
[[PreToolUse]]
matcher = "^Bash$"

[[PreToolUse.hooks]]
type = "command"
command = "python3 /tmp/pre.py"
timeout = 10
statusMessage = "checking"
"#,
    )
    .expect("hook events TOML should deserialize");

    assert_eq!(
        parsed,
        HookEventsToml {
            pre_tool_use: vec![MatcherGroup {
                matcher: Some("^Bash$".to_string()),
                hooks: vec![HookHandlerConfig::Command {
                    command: "python3 /tmp/pre.py".to_string(),
                    command_windows: None,
                    timeout_sec: Some(10),
                    r#async: false,
                    status_message: Some("checking".to_string()),
                }],
            }],
            ..Default::default()
        }
    );
}

#[test]
fn hooks_toml_deserializes_inline_events_and_state_map() {
    let parsed: HooksToml = toml::from_str(
        r#"
[state."/tmp/hooks.json:pre_tool_use:0:0"]
enabled = false
trusted_hash = "sha256:abc123"

[[PreToolUse]]
matcher = "^Bash$"

[[PreToolUse.hooks]]
type = "command"
command = "python3 /tmp/pre.py"
"#,
    )
    .expect("hooks TOML should deserialize");

    assert_eq!(
        parsed,
        HooksToml {
            events: HookEventsToml {
                pre_tool_use: vec![MatcherGroup {
                    matcher: Some("^Bash$".to_string()),
                    hooks: vec![HookHandlerConfig::Command {
                        command: "python3 /tmp/pre.py".to_string(),
                        command_windows: None,
                        timeout_sec: None,
                        r#async: false,
                        status_message: None,
                    }],
                }],
                ..Default::default()
            },
            state: BTreeMap::from([(
                "/tmp/hooks.json:pre_tool_use:0:0".to_string(),
                super::HookStateToml {
                    enabled: Some(false),
                    trusted_hash: Some("sha256:abc123".to_string()),
                },
            )]),
        }
    );
}

#[test]
fn managed_hooks_requirements_flatten_hook_events() {
    let parsed: ManagedHooksRequirementsToml = toml::from_str(
        r#"
managed_dir = "/enterprise/place"

[[PreToolUse]]
matcher = "^Bash$"

[[PreToolUse.hooks]]
type = "command"
command = "python3 /enterprise/place/pre.py"
"#,
    )
    .expect("requirements hooks TOML should deserialize");

    assert_eq!(
        parsed,
        ManagedHooksRequirementsToml {
            managed_dir: Some(std::path::PathBuf::from("/enterprise/place")),
            windows_managed_dir: None,
            hooks: HookEventsToml {
                pre_tool_use: vec![MatcherGroup {
                    matcher: Some("^Bash$".to_string()),
                    hooks: vec![HookHandlerConfig::Command {
                        command: "python3 /enterprise/place/pre.py".to_string(),
                        command_windows: None,
                        timeout_sec: None,
                        r#async: false,
                        status_message: None,
                    }],
                }],
                ..Default::default()
            },
        }
    );
}

#[test]
fn hook_events_deserialize_windows_override_from_toml() {
    let parsed: HookEventsToml = toml::from_str(
        r#"
[[PreToolUse]]
matcher = "^Bash$"

[[PreToolUse.hooks]]
type = "command"
command = "bash /enterprise/hooks/pre.sh"
command_windows = "powershell -File C:\\enterprise\\hooks\\pre.ps1"
"#,
    )
    .expect("hook command Windows override TOML should deserialize");

    assert_eq!(
        parsed,
        HookEventsToml {
            pre_tool_use: vec![MatcherGroup {
                matcher: Some("^Bash$".to_string()),
                hooks: vec![HookHandlerConfig::Command {
                    command: "bash /enterprise/hooks/pre.sh".to_string(),
                    command_windows: Some(
                        r"powershell -File C:\enterprise\hooks\pre.ps1".to_string(),
                    ),
                    timeout_sec: None,
                    r#async: false,
                    status_message: None,
                }],
            }],
            ..Default::default()
        }
    );
}

#[test]
fn hook_events_deserialize_camel_case_windows_override_from_toml() {
    let parsed: HookEventsToml = toml::from_str(
        r#"
[[PreToolUse]]
matcher = "^Bash$"

[[PreToolUse.hooks]]
type = "command"
command = "bash /enterprise/hooks/pre.sh"
commandWindows = "powershell -File C:\\enterprise\\hooks\\pre.ps1"
"#,
    )
    .expect("camelCase hook command Windows override TOML should deserialize");

    assert_eq!(
        parsed,
        HookEventsToml {
            pre_tool_use: vec![MatcherGroup {
                matcher: Some("^Bash$".to_string()),
                hooks: vec![HookHandlerConfig::Command {
                    command: "bash /enterprise/hooks/pre.sh".to_string(),
                    command_windows: Some(
                        r"powershell -File C:\enterprise\hooks\pre.ps1".to_string(),
                    ),
                    timeout_sec: None,
                    r#async: false,
                    status_message: None,
                }],
            }],
            ..Default::default()
        }
    );
}
