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

#[derive(Parseable, Debug, PartialEq)]
enum TestAction {
    /// @render 阅读收件箱
    ReadMsg,

    /// @render 记录思考
    Thinking {
        /// @render 思考内容
        content: String,
    },

    /// @render 什么都不做
    Idle {
        /// @render 等待秒数
        timeout_secs: Option<u64>,
    },

    /// @render 寄出信件
    SendMsg {
        /// @render 收件人
        recipient: String,
        /// @render 信件内容
        content: String,
    },

    /// @render 搜索替换文件内容
    ReplaceInFile {
        /// @render 文件路径
        path: String,
        /// @render 搜索文本
        search: String,
        /// @render 替换文本
        replace: String,
    },
}

#[test]
fn test_schema_markdown() {
    let schema = TestAction::schema_markdown("abc123");

    // Should contain element separator format
    assert!(schema.contains("TestAction-abc123"), "schema should contain element separator");

    // Should contain variant names
    assert!(schema.contains("read_msg"), "schema should contain read_msg");
    assert!(schema.contains("thinking"), "schema should contain thinking");
    assert!(schema.contains("idle"), "schema should contain idle");
    assert!(schema.contains("send_msg"), "schema should contain send_msg");
    assert!(schema.contains("replace_in_file"), "schema should contain replace_in_file");

    // Should contain doc comments
    assert!(schema.contains("// 阅读收件箱"), "schema should contain doc for ReadMsg");
    assert!(schema.contains("// 记录思考"), "schema should contain doc for Thinking");

    // Should contain field separators for multi-field variants
    assert!(schema.contains("recipient-abc123"), "schema should contain field separator");
    assert!(schema.contains("content-abc123"), "schema should contain field separator");

    // Should contain end marker explanation
    assert!(schema.contains("TestAction-end-abc123"), "schema should contain end marker");
}

#[test]
fn test_schema_contains_end_marker() {
    let schema = TestAction::schema_markdown("tok42");
    // End marker should be at the end of schema
    assert!(schema.contains("TestAction-end-tok42"), "schema must mention end marker");
}

#[test]
fn test_zero_field_parse() {
    let input = "TestAction-abc123\nread_msg\nTestAction-end-abc123";
    let result = TestAction::from_markdown(input, "abc123").unwrap();
    assert_eq!(result.len(), 1);
    assert_eq!(result[0], TestAction::ReadMsg);
}

#[test]
fn test_one_field_parse() {
    let input = "TestAction-abc123\nthinking\ncontent-abc123\n这是思考内容\n可以多行\nTestAction-end-abc123";
    let result = TestAction::from_markdown(input, "abc123").unwrap();
    assert_eq!(result.len(), 1);
    assert_eq!(result[0], TestAction::Thinking {
        content: "这是思考内容\n可以多行".to_string(),
    });
}

#[test]
fn test_option_field_with_value() {
    let input = "TestAction-abc123\nidle\ntimeout_secs-abc123\n120\nTestAction-end-abc123";
    let result = TestAction::from_markdown(input, "abc123").unwrap();
    assert_eq!(result.len(), 1);
    assert_eq!(result[0], TestAction::Idle {
        timeout_secs: Some(120),
    });
}

#[test]
fn test_option_field_none() {
    let input = "TestAction-abc123\nidle\nTestAction-end-abc123";
    let result = TestAction::from_markdown(input, "abc123").unwrap();
    assert_eq!(result.len(), 1);
    assert_eq!(result[0], TestAction::Idle {
        timeout_secs: None,
    });
}

#[test]
fn test_two_field_parse() {
    let input = "\
TestAction-abc123
send_msg
recipient-abc123
user
content-abc123
你好
这是多行内容
TestAction-end-abc123";
    let result = TestAction::from_markdown(input, "abc123").unwrap();
    assert_eq!(result.len(), 1);
    assert_eq!(result[0], TestAction::SendMsg {
        recipient: "user".to_string(),
        content: "你好\n这是多行内容".to_string(),
    });
}

#[test]
fn test_three_field_parse() {
    let input = "\
TestAction-abc123
replace_in_file
path-abc123
src/main.rs
search-abc123
old code
replace-abc123
new code
TestAction-end-abc123";
    let result = TestAction::from_markdown(input, "abc123").unwrap();
    assert_eq!(result.len(), 1);
    assert_eq!(result[0], TestAction::ReplaceInFile {
        path: "src/main.rs".to_string(),
        search: "old code".to_string(),
        replace: "new code".to_string(),
    });
}

#[test]
fn test_multi_action_parse() {
    let input = "\
TestAction-abc123
thinking
content-abc123
这是思考

TestAction-abc123
send_msg
recipient-abc123
user
content-abc123
你好

TestAction-abc123
idle
timeout_secs-abc123
120
TestAction-end-abc123";
    let result = TestAction::from_markdown(input, "abc123").unwrap();
    assert_eq!(result.len(), 3);
    assert_eq!(result[0], TestAction::Thinking {
        content: "这是思考".to_string(),
    });
    assert_eq!(result[1], TestAction::SendMsg {
        recipient: "user".to_string(),
        content: "你好".to_string(),
    });
    assert_eq!(result[2], TestAction::Idle {
        timeout_secs: Some(120),
    });
}

#[test]
fn test_end_marker_present() {
    // Valid: has end marker
    let input = "TestAction-abc123\nread_msg\nTestAction-end-abc123";
    let result = TestAction::from_markdown(input, "abc123");
    assert!(result.is_ok());
}

#[test]
fn test_end_marker_missing_truncation() {
    // Invalid: missing end marker → truncation error
    let input = "TestAction-abc123\nread_msg";
    let result = TestAction::from_markdown(input, "abc123");
    assert!(result.is_err());
    let err = result.unwrap_err();
    assert!(err.contains("truncated") || err.contains("end marker"),
        "Error should mention truncation: {}", err);
}

#[test]
fn test_end_marker_missing_multi_action() {
    // Multiple actions but no end marker
    let input = "\
TestAction-abc123
thinking
content-abc123
some thought

TestAction-abc123
read_msg";
    let result = TestAction::from_markdown(input, "abc123");
    assert!(result.is_err());
}

#[test]
fn test_schema_roundtrip() {
    let _schema = TestAction::schema_markdown("test42");

    // The schema itself should be parseable if we construct valid input from it
    let input = "\
TestAction-test42
read_msg

TestAction-test42
thinking
content-test42
一段思考内容

TestAction-test42
send_msg
recipient-test42
user
content-test42
信件内容

TestAction-test42
idle

TestAction-test42
replace_in_file
path-test42
src/lib.rs
search-test42
old
replace-test42
new
TestAction-end-test42";

    let result = TestAction::from_markdown(input, "test42").unwrap();
    assert_eq!(result.len(), 5);
    assert_eq!(result[0], TestAction::ReadMsg);
    assert_eq!(result[1], TestAction::Thinking { content: "一段思考内容".to_string() });
    assert_eq!(result[2], TestAction::SendMsg {
        recipient: "user".to_string(),
        content: "信件内容".to_string(),
    });
    assert_eq!(result[3], TestAction::Idle { timeout_secs: None });
    assert_eq!(result[4], TestAction::ReplaceInFile {
        path: "src/lib.rs".to_string(),
        search: "old".to_string(),
        replace: "new".to_string(),
    });
}


// === Tests for Option field comment-value defense ===

#[test]
fn test_option_field_comment_value_treated_as_none() {
    // Idle has timeout_secs: Option<u64>
    // If LLM outputs the schema comment as value, it should be None
    let input = "TestAction-cv1\nidle\ntimeout_secs-cv1\n// 等待秒数（可选）\nTestAction-end-cv1";
    let result = TestAction::from_markdown(input, "cv1").unwrap();
    assert_eq!(result.len(), 1);
    assert_eq!(result[0], TestAction::Idle { timeout_secs: None });
}

#[test]
fn test_option_field_any_comment_treated_as_none() {
    let input = "TestAction-cv2\nidle\ntimeout_secs-cv2\n// 任何注释\nTestAction-end-cv2";
    let result = TestAction::from_markdown(input, "cv2").unwrap();
    assert_eq!(result.len(), 1);
    assert_eq!(result[0], TestAction::Idle { timeout_secs: None });
}

#[test]
fn test_option_field_normal_value_still_works() {
    let input = "TestAction-cv3\nidle\ntimeout_secs-cv3\n120\nTestAction-end-cv3";
    let result = TestAction::from_markdown(input, "cv3").unwrap();
    assert_eq!(result.len(), 1);
    assert_eq!(result[0], TestAction::Idle { timeout_secs: Some(120) });
}

#[derive(Parseable, Debug, PartialEq)]
enum CommentTestAction {
    /// @render 测试
    Test {
        /// @render 名称
        name: Option<String>,
        /// @render 必填内容
        content: String,
    },
}

#[test]
fn test_option_string_field_comment_treated_as_none() {
    let input = "CommentTestAction-cv4\ntest\nname-cv4\n// 显示名称（可选）\ncontent-cv4\nhello\nCommentTestAction-end-cv4";
    let result = CommentTestAction::from_markdown(input, "cv4").unwrap();
    assert_eq!(result.len(), 1);
    assert_eq!(result[0], CommentTestAction::Test { name: None, content: "hello".to_string() });
}

#[test]
fn test_required_field_with_comment_prefix_not_affected() {
    // Non-optional field should keep the value even if it starts with //
    let input = "CommentTestAction-cv5\ntest\nname-cv5\nreal_name\ncontent-cv5\n// this is content\nCommentTestAction-end-cv5";
    let result = CommentTestAction::from_markdown(input, "cv5").unwrap();
    assert_eq!(result.len(), 1);
    assert_eq!(result[0], CommentTestAction::Test { name: Some("real_name".to_string()), content: "// this is content".to_string() });
}

// === Tests for ordered parse + default required ===

#[derive(Parseable, Debug, PartialEq)]
struct OrderedStruct {
    /// @render 名称
    name: String,
    /// @render 描述
    description: Option<String>,
    /// @render 内容
    content: String,
}

// --- Enum: out-of-order detection (2-field: content before recipient) ---

#[test]
fn test_enum_out_of_order_fields() {
    // SendMsg expects: recipient(0), content(1)
    // Output has content first, recipient second
    // Phase 2 sorted by position: content(fi=1), recipient(fi=0)
    // First iteration: fi=1, prev=None → skipped check: recipient(0) not optional → "missing before"
    let input = "TestAction-oo1\nsend_msg\ncontent-oo1\nhello\nrecipient-oo1\nuser\nTestAction-end-oo1";
    let result = TestAction::from_markdown(input, "oo1");
    assert!(result.is_err(), "Should detect out-of-order fields");
    let err = result.unwrap_err();
    assert!(err.contains("recipient") && (err.contains("missing") || err.contains("before")), "Error: {}", err);
}

// --- Enum: true order violation (3-field: path, replace, search → fi goes 0,2,1) ---

#[test]
fn test_enum_order_violation_three_fields() {
    // ReplaceInFile expects: path(0), search(1), replace(2)
    // Output: path, replace, search → sorted by pos: path(0), replace(2), search(1)
    // Processing: path(0) ok → replace(2): skip search(1) not optional → "missing between"
    let input = "TestAction-ov1\nreplace_in_file\npath-ov1\ntest.rs\nreplace-ov1\nnew\nsearch-ov1\nold\nTestAction-end-ov1";
    let result = TestAction::from_markdown(input, "ov1");
    assert!(result.is_err(), "Should detect order violation");
    let err = result.unwrap_err();
    assert!(err.contains("search") && err.contains("missing"), "Error: {}", err);
}

// --- Enum: required field missing separator ---

#[test]
fn test_enum_required_field_missing() {
    // SendMsg needs recipient and content, but only provide recipient
    let input = "TestAction-rm1\nsend_msg\nrecipient-rm1\nuser\nTestAction-end-rm1";
    let result = TestAction::from_markdown(input, "rm1");
    assert!(result.is_err(), "Should detect missing required field");
    let err = result.unwrap_err();
    assert!(err.contains("content") && err.contains("missing"), "Error: {}", err);
}

// --- Enum: required field empty value ---

#[test]
fn test_enum_required_field_empty_value() {
    // SendMsg with recipient present but content empty
    let input = "TestAction-re1\nsend_msg\nrecipient-re1\nuser\ncontent-re1\n\nTestAction-end-re1";
    let result = TestAction::from_markdown(input, "re1");
    assert!(result.is_err(), "Should detect empty required field");
    let err = result.unwrap_err();
    assert!(err.contains("content") && err.contains("empty"), "Error: {}", err);
}

// --- Enum: optional field skipped is OK ---

#[test]
fn test_enum_optional_field_skipped_ok() {
    // CommentTestAction::Test has name(Optional) and content(required)
    // Skip name separator entirely, only provide content
    let input = "CommentTestAction-os1\ntest\ncontent-os1\nhello world\nCommentTestAction-end-os1";
    let result = CommentTestAction::from_markdown(input, "os1");
    assert!(result.is_ok(), "Optional field skip should be OK: {:?}", result.err());
    let actions = result.unwrap();
    assert_eq!(actions.len(), 1);
    assert_eq!(actions[0], CommentTestAction::Test { name: None, content: "hello world".to_string() });
}

// --- Struct: normal order ---

#[test]
fn test_struct_ordered_parse_normal() {
    let input = "OrderedStruct-sp1\nname-sp1\nAlice\ndescription-sp1\nA test\ncontent-sp1\nHello world\nOrderedStruct-end-sp1";
    let result = OrderedStruct::from_markdown(input, "sp1").unwrap();
    assert_eq!(result.len(), 1);
    assert_eq!(result[0], OrderedStruct { name: "Alice".to_string(), description: Some("A test".to_string()), content: "Hello world".to_string() });
}

// --- Struct: optional skipped ---

#[test]
fn test_struct_optional_skipped() {
    // Skip description (Optional), only name and content
    let input = "OrderedStruct-sp2\nname-sp2\nAlice\ncontent-sp2\nHello world\nOrderedStruct-end-sp2";
    let result = OrderedStruct::from_markdown(input, "sp2");
    assert!(result.is_ok(), "Optional skip should be OK: {:?}", result.err());
    let items = result.unwrap();
    assert_eq!(items.len(), 1);
    assert_eq!(items[0], OrderedStruct { name: "Alice".to_string(), description: None, content: "Hello world".to_string() });
}

// --- Struct: out-of-order ---

#[test]
fn test_struct_out_of_order() {
    // content before name
    let input = "OrderedStruct-sp3\ncontent-sp3\nHello\nname-sp3\nAlice\nOrderedStruct-end-sp3";
    let result = OrderedStruct::from_markdown(input, "sp3");
    assert!(result.is_err(), "Should detect out-of-order");
    let err = result.unwrap_err();
    assert!(err.contains("name") && (err.contains("missing") || err.contains("before")), "Error: {}", err);
}

// --- Struct: required field missing ---

#[test]
fn test_struct_required_field_missing() {
    // Only name, content is missing (required)
    let input = "OrderedStruct-sp4\nname-sp4\nAlice\nOrderedStruct-end-sp4";
    let result = OrderedStruct::from_markdown(input, "sp4");
    assert!(result.is_err(), "Should detect missing required field");
    let err = result.unwrap_err();
    assert!(err.contains("content") && err.contains("missing"), "Error: {}", err);
}

// --- Struct: required field empty ---

#[test]
fn test_struct_required_field_empty() {
    let input = "OrderedStruct-sp5\nname-sp5\n\ncontent-sp5\nHello\nOrderedStruct-end-sp5";
    let result = OrderedStruct::from_markdown(input, "sp5");
    assert!(result.is_err(), "Should detect empty required field");
    let err = result.unwrap_err();
    assert!(err.contains("name") && err.contains("empty"), "Error: {}", err);
}

// --- Struct: all required fields missing ---

#[test]
fn test_struct_all_required_missing() {
    let input = "OrderedStruct-sp6\ndescription-sp6\nsome desc\nOrderedStruct-end-sp6";
    let result = OrderedStruct::from_markdown(input, "sp6");
    assert!(result.is_err(), "Should detect missing required fields");
    let err = result.unwrap_err();
    assert!(err.contains("name") && err.contains("missing"), "Error: {}", err);
}
