use mad_hatter::llm::apply_regex_extraction;

// ============================================================
// Unit tests for apply_regex_extraction
// ============================================================

#[test]
fn test_regex_extraction_with_capture_group() {
    let result = apply_regex_extraction("进化二号（引擎）(ebc381)", &[r"\((\w+)\)$"]);
    assert_eq!(result, "ebc381");
}

#[test]
fn test_regex_extraction_without_capture_group() {
    let result = apply_regex_extraction("hello world 123", &[r"\d+"]);
    assert_eq!(result, "123");
}

#[test]
fn test_regex_extraction_first_pattern_matches() {
    // First pattern matches, second is not tried
    let result = apply_regex_extraction("abc-123-def", &[r"(\d+)", r"(\w+)"]);
    assert_eq!(result, "123");
}

#[test]
fn test_regex_extraction_fallback_to_second_pattern() {
    // First pattern doesn't match, second does
    let result = apply_regex_extraction("hello", &[r"\d+", r"(\w+)"]);
    assert_eq!(result, "hello");
}

#[test]
fn test_regex_extraction_no_match_preserves_original() {
    let result = apply_regex_extraction("hello world", &[r"^\d+$", r"^[A-Z]+$"]);
    assert_eq!(result, "hello world");
}

#[test]
fn test_regex_extraction_empty_patterns() {
    let result = apply_regex_extraction("hello", &[]);
    assert_eq!(result, "hello");
}

// ============================================================
// Integration tests with Parseable derive
// ============================================================

mod enum_tests {
    use mad_hatter::llm::Parseable;

    #[derive(mad_hatter::Parseable, Debug, PartialEq)]
    enum TestAction {
        /// @render 发送消息
        SendMsg {
            /// @render 收件人
            #[parse(regex = [r"\((\w+)\)$", r"(\S+)"])]
            recipient: String,
            /// @render 内容
            content: String,
        },
        /// @render 设置参数
        SetParam {
            /// @render 名称
            name: String,
            /// @render 值
            #[parse(regex = [r"^(\d+)"])]
            value: Option<String>,
        },
    }

    #[test]
    fn test_enum_regex_extraction_with_capture_group() {
        let token = "abc123";
        let input = format!(
            "TestAction-{token}\nsend_msg\nrecipient-{token}\n进化二号（引擎）(ebc381)\ncontent-{token}\nhello there\nTestAction-end-{token}",
            token = token
        );
        let results: Vec<TestAction> = TestAction::from_markdown(&input, token).unwrap();
        assert_eq!(results.len(), 1);
        match &results[0] {
            TestAction::SendMsg { recipient, content } => {
                assert_eq!(recipient, "ebc381");
                assert_eq!(content, "hello there");
            }
            _ => panic!("unexpected variant"),
        }
    }

    #[test]
    fn test_enum_regex_no_match_preserves_original() {
        let token = "abc123";
        let input = format!(
            "TestAction-{token}\nsend_msg\nrecipient-{token}\nplain_name\ncontent-{token}\nhello\nTestAction-end-{token}",
            token = token
        );
        let results: Vec<TestAction> = TestAction::from_markdown(&input, token).unwrap();
        match &results[0] {
            TestAction::SendMsg { recipient, .. } => {
                assert_eq!(recipient, "plain_name");
            }
            _ => panic!("unexpected variant"),
        }
    }

    #[test]
    fn test_enum_option_field_regex_extraction() {
        let token = "abc123";
        let input = format!(
            "TestAction-{token}\nset_param\nname-{token}\ntimeout\nvalue-{token}\n42 seconds\nTestAction-end-{token}",
            token = token
        );
        let results: Vec<TestAction> = TestAction::from_markdown(&input, token).unwrap();
        match &results[0] {
            TestAction::SetParam { name, value } => {
                assert_eq!(name, "timeout");
                assert_eq!(value, &Some("42".to_string()));
            }
            _ => panic!("unexpected variant"),
        }
    }

    #[test]
    fn test_enum_option_field_none_not_affected() {
        let token = "abc123";
        let input = format!(
            "TestAction-{token}\nset_param\nname-{token}\ntimeout\nvalue-{token}\n\nTestAction-end-{token}",
            token = token
        );
        let results: Vec<TestAction> = TestAction::from_markdown(&input, token).unwrap();
        match &results[0] {
            TestAction::SetParam { value, .. } => {
                assert_eq!(value, &None);
            }
            _ => panic!("unexpected variant"),
        }
    }
}

mod struct_tests {
    use mad_hatter::llm::Parseable;

    #[derive(mad_hatter::Parseable, Debug, PartialEq)]
    struct Profile {
        /// @render 用户ID
        #[parse(regex = [r"\((\w+)\)$"])]
        user_id: String,
        /// @render 显示名称
        display_name: String,
        /// @render 标签
        #[parse(regex = [r"#(\w+)"])]
        tag: Option<String>,
    }

    #[test]
    fn test_struct_regex_extraction() {
        let token = "def456";
        let input = format!(
            "Profile-{token}\nuser_id-{token}\n用户A(abc123)\ndisplay_name-{token}\nAlice\ntag-{token}\n#admin\nProfile-end-{token}",
            token = token
        );
        let results: Vec<Profile> = Profile::from_markdown(&input, token).unwrap();
        assert_eq!(results.len(), 1);
        assert_eq!(results[0].user_id, "abc123");
        assert_eq!(results[0].display_name, "Alice");
        assert_eq!(results[0].tag, Some("admin".to_string()));
    }

    #[test]
    fn test_struct_regex_no_match_preserves() {
        let token = "def456";
        let input = format!(
            "Profile-{token}\nuser_id-{token}\nplain_id\ndisplay_name-{token}\nBob\ntag-{token}\nnotag\nProfile-end-{token}",
            token = token
        );
        let results: Vec<Profile> = Profile::from_markdown(&input, token).unwrap();
        assert_eq!(results[0].user_id, "plain_id");
        // #(\w+) doesn't match "notag" - no # prefix
        assert_eq!(results[0].tag, Some("notag".to_string()));
    }

    #[test]
    fn test_struct_option_none_not_affected() {
        let token = "def456";
        let input = format!(
            "Profile-{token}\nuser_id-{token}\nuser(xyz)\ndisplay_name-{token}\nCharlie\ntag-{token}\n\nProfile-end-{token}",
            token = token
        );
        let results: Vec<Profile> = Profile::from_markdown(&input, token).unwrap();
        assert_eq!(results[0].user_id, "xyz");
        assert_eq!(results[0].tag, None);
    }

    #[test]
    fn test_field_without_regex_not_affected() {
        let token = "def456";
        let input = format!(
            "Profile-{token}\nuser_id-{token}\ntest(id1)\ndisplay_name-{token}\nName with (parens)\ntag-{token}\n#tag1\nProfile-end-{token}",
            token = token
        );
        let results: Vec<Profile> = Profile::from_markdown(&input, token).unwrap();
        assert_eq!(results[0].user_id, "id1");
        // display_name has no regex, so parens are preserved
        assert_eq!(results[0].display_name, "Name with (parens)");
        assert_eq!(results[0].tag, Some("tag1".to_string()));
    }
}
