use mad_hatter::Sealed;
use mad_hatter::Parseable;
use mad_hatter::Promptable;
use mad_hatter::llm::Parseable as _;
use mad_hatter::llm::Promptable as _;
use mad_hatter::llm::Parseable;

// Sealed constants for testing
const SENDER_USER: Sealed = Sealed::new("user");
const SENDER_SYSTEM: Sealed = Sealed::new("system");

// ============================================================
// Struct tests
// ============================================================

#[derive(Promptable, Parseable, Debug, PartialEq)]
struct MsgStruct {
    /// @render 收件人
    recipient: String,

    /// @render 内容
    body: String,

    #[derived(from = recipient, matches = SENDER_USER)]
    is_user: bool,

    #[derived(from = recipient, matches = SENDER_SYSTEM)]
    is_system: bool,
}

#[test]
fn test_struct_derived_schema_excludes_derived_fields() {
    let schema = MsgStruct::schema_markdown("test_token");
    assert!(schema.contains("recipient"), "schema should contain 'recipient'");
    assert!(schema.contains("body") || schema.contains("内容"), "schema should contain body field");
    assert!(!schema.contains("is_user"), "schema should NOT contain 'is_user'");
    assert!(!schema.contains("is_system"), "schema should NOT contain 'is_system'");
}

#[test]
fn test_struct_derived_to_markdown_excludes_derived_fields() {
    let msg = MsgStruct {
        recipient: "user".to_string(),
        body: "hello".to_string(),
        is_user: true,
        is_system: false,
    };
    let rendered = msg.to_markdown();
    // to_markdown renders doc comment text, not field names
    assert!(rendered.contains("收件人"), "rendered should contain doc comment for recipient");
    assert!(rendered.contains("内容"), "rendered should contain doc comment for body");
    assert!(!rendered.contains("is_user"), "rendered should NOT contain 'is_user'");
    assert!(!rendered.contains("is_system"), "rendered should NOT contain 'is_system'");
}

#[test]
fn test_struct_derived_match_true() {
    let token = "test_token";
    let input = format!(
        "MsgStruct-{token}\nrecipient-{token}\nuser\nbody-{token}\nhello world\nMsgStruct-end-{token}",
        token = token
    );
    let results = MsgStruct::from_markdown(&input, token).unwrap();
    assert_eq!(results.len(), 1);
    let result = &results[0];
    assert_eq!(result.recipient, "user");
    assert_eq!(result.body, "hello world");
    assert!(result.is_user, "is_user should be true when recipient is 'user'");
    assert!(!result.is_system, "is_system should be false when recipient is 'user'");
}

#[test]
fn test_struct_derived_match_false() {
    let token = "test_token";
    let input = format!(
        "MsgStruct-{token}\nrecipient-{token}\nagent_abc\nbody-{token}\nhello world\nMsgStruct-end-{token}",
        token = token
    );
    let results = MsgStruct::from_markdown(&input, token).unwrap();
    assert_eq!(results.len(), 1);
    let result = &results[0];
    assert_eq!(result.recipient, "agent_abc");
    assert!(!result.is_user, "is_user should be false when recipient is 'agent_abc'");
    assert!(!result.is_system, "is_system should be false when recipient is 'agent_abc'");
}

#[test]
fn test_struct_derived_system_match() {
    let token = "test_token";
    let input = format!(
        "MsgStruct-{token}\nrecipient-{token}\nsystem\nbody-{token}\nsome message\nMsgStruct-end-{token}",
        token = token
    );
    let results = MsgStruct::from_markdown(&input, token).unwrap();
    assert_eq!(results.len(), 1);
    let result = &results[0];
    assert_eq!(result.recipient, "system");
    assert!(!result.is_user, "is_user should be false when recipient is 'system'");
    assert!(result.is_system, "is_system should be true when recipient is 'system'");
}

// ============================================================
// Enum tests
// ============================================================

#[derive(Parseable, Debug, PartialEq)]
enum TestAction {
    /// @render 发送消息
    SendMsg {
        /// @render 收件人
        recipient: String,
        /// @render 内容
        body: String,

        #[derived(from = recipient, matches = SENDER_USER)]
        is_user: bool,
    },

    /// @render 空闲
    Idle,
}

#[test]
fn test_enum_derived_schema_excludes_derived_fields() {
    let schema = TestAction::schema_markdown("test_token");
    assert!(schema.contains("recipient"), "schema should contain 'recipient'");
    assert!(schema.contains("body") || schema.contains("内容"), "schema should contain body field");
    assert!(!schema.contains("is_user"), "schema should NOT contain 'is_user'");
}

#[test]
fn test_enum_derived_match_true() {
    let token = "test_token";
    let input = format!(
        "TestAction-{token}\nsend_msg\nrecipient-{token}\nuser\nbody-{token}\nhello\nTestAction-end-{token}",
        token = token
    );
    let results = TestAction::from_markdown(&input, token).unwrap();
    assert_eq!(results.len(), 1);
    match &results[0] {
        TestAction::SendMsg { recipient, body, is_user } => {
            assert_eq!(recipient, "user");
            assert_eq!(body, "hello");
            assert!(*is_user, "is_user should be true");
        }
        _ => panic!("expected SendMsg"),
    }
}

#[test]
fn test_enum_derived_match_false() {
    let token = "test_token";
    let input = format!(
        "TestAction-{token}\nsend_msg\nrecipient-{token}\nagent_xyz\nbody-{token}\nhello\nTestAction-end-{token}",
        token = token
    );
    let results = TestAction::from_markdown(&input, token).unwrap();
    assert_eq!(results.len(), 1);
    match &results[0] {
        TestAction::SendMsg { recipient, body, is_user } => {
            assert_eq!(recipient, "agent_xyz");
            assert_eq!(body, "hello");
            assert!(!*is_user, "is_user should be false");
        }
        _ => panic!("expected SendMsg"),
    }
}

// ============================================================
// Derived + regex combination
// ============================================================

#[derive(Parseable, Debug, PartialEq)]
enum RegexDerivedAction {
    /// @render 发送消息
    SendMsg {
        /// @render 收件人
        #[parse(regex = [r"\((\w+)\)$"])]
        recipient: String,
        /// @render 内容
        body: String,

        #[derived(from = recipient, matches = SENDER_USER)]
        is_user: bool,
    },
}

#[test]
fn test_enum_derived_with_regex_match() {
    // regex extracts "user" from "用户(user)", then derived matches SENDER_USER
    let token = "test_token";
    let input = format!(
        "RegexDerivedAction-{token}\nsend_msg\nrecipient-{token}\n用户(user)\nbody-{token}\nhello\nRegexDerivedAction-end-{token}",
        token = token
    );
    let results = RegexDerivedAction::from_markdown(&input, token).unwrap();
    assert_eq!(results.len(), 1);
    match &results[0] {
        RegexDerivedAction::SendMsg { recipient, body, is_user } => {
            assert_eq!(recipient, "user", "regex should extract 'user' from '用户(user)'");
            assert_eq!(body, "hello");
            assert!(*is_user, "is_user should be true after regex extraction");
        }
        _ => panic!("expected SendMsg"),
    }
}

#[test]
fn test_enum_derived_with_regex_no_match() {
    // regex extracts "agent123" from "代理(agent123)", doesn't match SENDER_USER
    let token = "test_token";
    let input = format!(
        "RegexDerivedAction-{token}\nsend_msg\nrecipient-{token}\n代理(agent123)\nbody-{token}\nhello\nRegexDerivedAction-end-{token}",
        token = token
    );
    let results = RegexDerivedAction::from_markdown(&input, token).unwrap();
    assert_eq!(results.len(), 1);
    match &results[0] {
        RegexDerivedAction::SendMsg { recipient, body, is_user } => {
            assert_eq!(recipient, "agent123", "regex should extract 'agent123'");
            assert_eq!(body, "hello");
            assert!(!*is_user, "is_user should be false for non-user recipient");
        }
        _ => panic!("expected SendMsg"),
    }
}

// ============================================================
// String literal matches tests
// ============================================================

#[derive(Promptable, Parseable, Debug, PartialEq)]
struct LiteralMatchStruct {
    /// @render 发送者
    sender: String,

    /// @render 内容
    body: String,

    #[derived(from = sender, matches = "user")]
    is_user: bool,

    #[derived(from = sender, matches = "system")]
    is_system: bool,
}

#[test]
fn test_struct_literal_match_true() {
    let token = "test_token";
    let input = format!(
        "LiteralMatchStruct-{token}\nsender-{token}\nuser\nbody-{token}\nhello\nLiteralMatchStruct-end-{token}",
        token = token
    );
    let results = LiteralMatchStruct::from_markdown(&input, token).unwrap();
    assert_eq!(results.len(), 1);
    let result = &results[0];
    assert_eq!(result.sender, "user");
    assert!(result.is_user, "is_user should be true when sender is 'user'");
    assert!(!result.is_system, "is_system should be false when sender is 'user'");
}

#[test]
fn test_struct_literal_match_false() {
    let token = "test_token";
    let input = format!(
        "LiteralMatchStruct-{token}\nsender-{token}\nagent\nbody-{token}\nhello\nLiteralMatchStruct-end-{token}",
        token = token
    );
    let results = LiteralMatchStruct::from_markdown(&input, token).unwrap();
    assert_eq!(results.len(), 1);
    let result = &results[0];
    assert!(!result.is_user, "is_user should be false for 'agent'");
    assert!(!result.is_system, "is_system should be false for 'agent'");
}

#[test]
fn test_struct_literal_match_system() {
    let token = "test_token";
    let input = format!(
        "LiteralMatchStruct-{token}\nsender-{token}\nsystem\nbody-{token}\nhello\nLiteralMatchStruct-end-{token}",
        token = token
    );
    let results = LiteralMatchStruct::from_markdown(&input, token).unwrap();
    assert_eq!(results.len(), 1);
    let result = &results[0];
    assert!(!result.is_user);
    assert!(result.is_system, "is_system should be true when sender is 'system'");
}

#[derive(Parseable, Debug, PartialEq)]
enum LiteralMatchAction {
    /// @render 发送消息
    SendMsg {
        /// @render 收件人
        recipient: String,
        /// @render 内容
        body: String,

        #[derived(from = recipient, matches = "user")]
        is_user: bool,
    },
}

#[test]
fn test_enum_literal_match_true() {
    let token = "test_token";
    let input = format!(
        "LiteralMatchAction-{token}\nsend_msg\nrecipient-{token}\nuser\nbody-{token}\nhello\nLiteralMatchAction-end-{token}",
        token = token
    );
    let results = LiteralMatchAction::from_markdown(&input, token).unwrap();
    assert_eq!(results.len(), 1);
    match &results[0] {
        LiteralMatchAction::SendMsg { recipient, body, is_user } => {
            assert_eq!(recipient, "user");
            assert_eq!(body, "hello");
            assert!(*is_user, "is_user should be true");
        }
    }
}

#[test]
fn test_enum_literal_match_false() {
    let token = "test_token";
    let input = format!(
        "LiteralMatchAction-{token}\nsend_msg\nrecipient-{token}\nagent_xyz\nbody-{token}\nhello\nLiteralMatchAction-end-{token}",
        token = token
    );
    let results = LiteralMatchAction::from_markdown(&input, token).unwrap();
    assert_eq!(results.len(), 1);
    match &results[0] {
        LiteralMatchAction::SendMsg { recipient, body, is_user } => {
            assert_eq!(recipient, "agent_xyz");
            assert_eq!(body, "hello");
            assert!(!*is_user, "is_user should be false");
        }
    }
}