use super::parser::QuotedString;
use super::tokenizer::{find_first_equals_outside_quotes, tokenize_with_equals};

#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct ParsedArguments {
    pub args: Vec<QuotedString>,
    pub kwargs: Vec<(QuotedString, QuotedString)>,
}

/// Process escaped sequences in a string.
/// Converts `\"` to `"` and `\\` to `\`.
pub fn process_escapes(s: &str) -> String {
    let mut result = String::new();
    let mut chars = s.chars().peekable();

    while let Some(ch) = chars.next() {
        if ch == '\\' {
            if let Some(&next_ch) = chars.peek() {
                match next_ch {
                    '"' => {
                        result.push('"');
                        chars.next(); // consume the quote
                    }
                    '\\' => {
                        result.push('\\');
                        chars.next(); // consume the backslash
                    }
                    _ => {
                        result.push(ch);
                    }
                }
            } else {
                result.push(ch);
            }
        } else {
            result.push(ch);
        }
    }

    result
}

/// Simple approach: find quoted segments in original input
fn find_quoted_segments(input: &str) -> Vec<(usize, usize, String)> {
    let mut segments = Vec::new();
    let mut chars = input.char_indices().peekable();

    while let Some((i, ch)) = chars.next() {
        if ch == '"' {
            let start = i;
            let mut content = String::new();
            let mut escaped = false;

            // Read until closing quote or end
            for (j, inner_ch) in chars.by_ref() {
                if inner_ch == '"' && !escaped {
                    // Found closing quote
                    segments.push((start, j + 1, content));
                    break;
                } else if inner_ch == '\\' && !escaped {
                    escaped = true;
                    content.push(inner_ch);
                } else {
                    escaped = false;
                    content.push(inner_ch);
                }
            }
        }
    }

    segments
}

/// Check if a token's content was originally in quotes (and was cleanly quoted)
fn was_token_quoted(input: &str, token: &str) -> bool {
    let quoted_segments = find_quoted_segments(input);

    for (_, _, content) in quoted_segments {
        // Simple processing: handle basic escapes
        let processed = content.replace("\\\"", "\"").replace("\\\\", "\\");
        if processed == token {
            // Only return true if the content doesn't contain escape sequences
            return !content.contains("\\\"") && !content.contains("\\\\");
        }
    }

    false
}

/// Honey-badger style argument parser that recovers from all errors.
/// Handles both positional args and key=value pairs.
/// Args, keys, and values can have spaces if quoted.
/// Recovers gracefully from unclosed quotes, malformed key=value pairs, etc.
#[must_use]
pub fn parse_arguments(input: &str) -> ParsedArguments {
    let mut args = Vec::new();
    let mut kwargs = Vec::new();

    // Use the original robust tokenizer
    let tokens = tokenize_with_equals(input);

    for token in tokens {
        // Look for key=value pattern (find first = that's not inside quotes)
        if let Some(eq_pos) = find_first_equals_outside_quotes(&token) {
            let key_part = &token[..eq_pos];
            let value_part = &token[eq_pos + 1..];

            // Check if key and value were originally quoted
            let key_quoted = was_token_quoted(input, key_part);
            let value_quoted = was_token_quoted(input, value_part);

            // Process the content with escape handling
            let key_content = process_escapes(key_part);
            let value_content = process_escapes(value_part);

            // Special case: if the value starts with '=', and the key is empty,
            // treat it as empty key with empty value (honey-badger recovery)
            if key_content.is_empty() && value_content.starts_with('=') {
                kwargs.push((
                    QuotedString::new(key_content, key_quoted),
                    QuotedString::new(String::new(), false),
                ));
            } else {
                kwargs.push((
                    QuotedString::new(key_content, key_quoted),
                    QuotedString::new(value_content, value_quoted),
                ));
            }
        } else {
            // This is a positional argument
            let quoted = was_token_quoted(input, &token);
            let content = process_escapes(&token);
            args.push(QuotedString::new(content, quoted));
        }
    }

    ParsedArguments { args, kwargs }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_parse_arguments_empty() {
        let result = parse_arguments("");
        assert_eq!(result.args.len(), 0);
        assert_eq!(result.kwargs.len(), 0);
    }

    #[test]
    fn test_parse_arguments_single_arg() {
        let result = parse_arguments("hello");
        assert_eq!(result.args.len(), 1);
        assert_eq!(result.args[0].content, "hello");
        assert!(!result.args[0].quoted);
        assert_eq!(result.kwargs.len(), 0);
    }

    #[test]
    fn test_parse_arguments_quoted_arg() {
        let result = parse_arguments("\"hello world\"");
        assert_eq!(result.args.len(), 1);
        assert_eq!(result.args[0].content, "hello world");
        assert!(result.args[0].quoted);
        assert_eq!(result.kwargs.len(), 0);
    }

    #[test]
    fn test_parse_arguments_multiple_args() {
        let result = parse_arguments("hello world test");
        assert_eq!(result.args.len(), 3);
        assert_eq!(result.args[0].content, "hello");
        assert_eq!(result.args[1].content, "world");
        assert_eq!(result.args[2].content, "test");
        assert!(!result.args[0].quoted);
        assert!(!result.args[1].quoted);
        assert!(!result.args[2].quoted);
        assert_eq!(result.kwargs.len(), 0);
    }

    #[test]
    fn test_parse_arguments_kwarg() {
        let result = parse_arguments("key=value");
        assert_eq!(result.args.len(), 0);
        assert_eq!(result.kwargs.len(), 1);
        assert_eq!(result.kwargs[0].0.content, "key");
        assert_eq!(result.kwargs[0].1.content, "value");
        assert!(!result.kwargs[0].0.quoted);
        assert!(!result.kwargs[0].1.quoted);
    }

    #[test]
    fn test_parse_arguments_quoted_kwarg_key() {
        let result = parse_arguments("\"my key\"=value");
        assert_eq!(result.args.len(), 0);
        assert_eq!(result.kwargs.len(), 1);
        assert_eq!(result.kwargs[0].0.content, "my key");
        assert_eq!(result.kwargs[0].1.content, "value");
        assert!(result.kwargs[0].0.quoted);
        assert!(!result.kwargs[0].1.quoted);
    }

    #[test]
    fn test_parse_arguments_quoted_kwarg_value() {
        let result = parse_arguments("key=\"my value\"");
        assert_eq!(result.args.len(), 0);
        assert_eq!(result.kwargs.len(), 1);
        assert_eq!(result.kwargs[0].0.content, "key");
        assert_eq!(result.kwargs[0].1.content, "my value");
        assert!(!result.kwargs[0].0.quoted);
        assert!(result.kwargs[0].1.quoted);
    }

    #[test]
    fn test_parse_arguments_mixed_args_and_kwargs() {
        let result = parse_arguments("arg1 \"arg 2\" key=value \"quoted key\"=\"quoted value\"");
        assert_eq!(result.args.len(), 2);
        assert_eq!(result.kwargs.len(), 2);

        assert_eq!(result.args[0].content, "arg1");
        assert!(!result.args[0].quoted);
        assert_eq!(result.args[1].content, "arg 2");
        assert!(result.args[1].quoted);

        assert_eq!(result.kwargs[0].0.content, "key");
        assert_eq!(result.kwargs[0].1.content, "value");
        assert!(!result.kwargs[0].0.quoted);
        assert!(!result.kwargs[0].1.quoted);

        assert_eq!(result.kwargs[1].0.content, "quoted key");
        assert_eq!(result.kwargs[1].1.content, "quoted value");
        assert!(result.kwargs[1].0.quoted);
        assert!(result.kwargs[1].1.quoted);
    }

    #[test]
    fn test_parse_arguments_escaped_quotes() {
        let result = parse_arguments("\"hello \\\"world\\\"\"");
        assert_eq!(result.args.len(), 1);
        assert_eq!(result.args[0].content, "hello \"world\"");
        // Note: this should be false because the content contains escape sequences
        assert!(!result.args[0].quoted);
    }

    #[test]
    fn test_parse_arguments_escaped_backslashes() {
        let result = parse_arguments("\"hello\\\\world\"");
        assert_eq!(result.args.len(), 1);
        assert_eq!(result.args[0].content, "hello\\world");
        // Note: this should be false because the content contains escape sequences
        assert!(!result.args[0].quoted);
    }

    #[test]
    fn test_parse_arguments_empty_kwarg_value() {
        let result = parse_arguments("key=");
        assert_eq!(result.args.len(), 0);
        assert_eq!(result.kwargs.len(), 1);
        assert_eq!(result.kwargs[0].0.content, "key");
        assert_eq!(result.kwargs[0].1.content, "");
        assert!(!result.kwargs[0].0.quoted);
        assert!(!result.kwargs[0].1.quoted);
    }

    #[test]
    fn test_parse_arguments_empty_kwarg_key() {
        let result = parse_arguments("=value");
        assert_eq!(result.args.len(), 0);
        assert_eq!(result.kwargs.len(), 1);
        assert_eq!(result.kwargs[0].0.content, "");
        assert_eq!(result.kwargs[0].1.content, "value");
        assert!(!result.kwargs[0].0.quoted);
        assert!(!result.kwargs[0].1.quoted);
    }

    #[test]
    fn test_parse_arguments_malformed_double_equals() {
        let result = parse_arguments("key==value");
        assert_eq!(result.args.len(), 0);
        assert_eq!(result.kwargs.len(), 1);
        assert_eq!(result.kwargs[0].0.content, "key");
        assert_eq!(result.kwargs[0].1.content, "=value");
        assert!(!result.kwargs[0].0.quoted);
        assert!(!result.kwargs[0].1.quoted);
    }

    #[test]
    fn test_parse_arguments_unclosed_quotes() {
        let result = parse_arguments("\"unclosed quote");
        assert_eq!(result.args.len(), 1);
        assert_eq!(result.args[0].content, "unclosed quote");
        assert!(!result.args[0].quoted); // Not cleanly quoted
    }

    #[test]
    fn test_process_escapes_simple() {
        assert_eq!(process_escapes("hello"), "hello");
        assert_eq!(process_escapes("hello world"), "hello world");
    }

    #[test]
    fn test_process_escapes_quote() {
        assert_eq!(process_escapes("hello \\\"world\\\""), "hello \"world\"");
    }

    #[test]
    fn test_process_escapes_backslash() {
        assert_eq!(process_escapes("hello\\\\world"), "hello\\world");
    }

    #[test]
    fn test_process_escapes_other_escape() {
        assert_eq!(process_escapes("hello\\nworld"), "hello\\nworld");
    }

    #[test]
    fn test_process_escapes_trailing_backslash() {
        assert_eq!(process_escapes("hello\\"), "hello\\");
    }

    #[test]
    fn test_find_quoted_segments_simple() {
        let segments = find_quoted_segments("\"hello world\"");
        assert_eq!(segments.len(), 1);
        assert_eq!(segments[0].2, "hello world");
    }

    #[test]
    fn test_find_quoted_segments_multiple() {
        let segments = find_quoted_segments("\"hello\" \"world\"");
        assert_eq!(segments.len(), 2);
        assert_eq!(segments[0].2, "hello");
        assert_eq!(segments[1].2, "world");
    }

    #[test]
    fn test_find_quoted_segments_escaped() {
        let segments = find_quoted_segments("\"hello \\\"world\\\"\"");
        assert_eq!(segments.len(), 1);
        assert_eq!(segments[0].2, "hello \\\"world\\\"");
    }

    #[test]
    fn test_was_token_quoted_simple() {
        assert!(was_token_quoted("\"hello\"", "hello"));
        assert!(!was_token_quoted("hello", "hello"));
    }

    #[test]
    fn test_was_token_quoted_escaped() {
        // Escaped content should return false
        assert!(!was_token_quoted(
            "\"hello \\\"world\\\"\"",
            "hello \"world\""
        ));
    }

    #[test]
    fn test_was_token_quoted_no_match() {
        assert!(!was_token_quoted("\"hello\"", "world"));
    }

    /// Test coverage exclusions for trivial code
    #[cfg(test)]
    mod coverage_exclusions {
        // The following functions/patterns are excluded from coverage requirements:

        // 1. ParsedArguments struct - trivial struct definition, covered by usage tests
        // 2. Default trait implementations - automatically generated
        // 3. Error handling paths that can't be easily triggered in unit tests
        // 4. Debug assertions that are only active in debug builds

        use super::*;

        #[test]
        fn test_parsed_arguments_serialization() {
            // Test that ParsedArguments can be serialized/deserialized
            let arguments = ParsedArguments {
                args: vec![QuotedString::new("test".to_string(), false)],
                kwargs: vec![(
                    QuotedString::new("key".to_string(), false),
                    QuotedString::new("value".to_string(), false),
                )],
            };

            let serialized = serde_json::to_string(&arguments).unwrap();
            let deserialized: ParsedArguments = serde_json::from_str(&serialized).unwrap();
            assert_eq!(arguments, deserialized);
        }
    }

    // ==============================================================================
    // Additional Tests for Specific Coverage Lines
    // ==============================================================================

    #[test]
    fn test_process_escapes_detailed() {
        // Test lines 11-13 - function start and iterator setup
        let result = process_escapes("simple");
        assert_eq!(result, "simple");

        // Test lines 15-18 - backslash handling with next character
        let result = process_escapes("test\\\"quoted");
        assert_eq!(result, "test\"quoted");

        // Test lines 20-21 - escaped quote case
        let result = process_escapes("\\\"");
        assert_eq!(result, "\"");

        // Test lines 24-25 - escaped backslash case
        let result = process_escapes("\\\\");
        assert_eq!(result, "\\");

        // Test line 28 - other escape character (not quote or backslash)
        let result = process_escapes("\\n");
        assert_eq!(result, "\\n");
    }

    #[test]
    fn test_process_escapes_edge_cases() {
        // Test line 32 - backslash at end of string (no next char)
        let result = process_escapes("test\\");
        assert_eq!(result, "test\\");

        // Test line 35 - normal character (not backslash)
        let result = process_escapes("normal");
        assert_eq!(result, "normal");

        // Test line 39 - result return
        let result = process_escapes("");
        assert_eq!(result, "");
    }

    #[test]
    fn test_find_quoted_segments_detailed() {
        // Test lines 43-45 - function start and setup
        let segments = find_quoted_segments("no quotes");
        assert_eq!(segments.len(), 0);

        // Test lines 47-48 - finding quote character
        let segments = find_quoted_segments("\"test\"");
        assert_eq!(segments.len(), 1);
        assert_eq!(segments[0].2, "test");

        // Test lines 50-51 - quote found, initialize content and escaped flag
        let segments = find_quoted_segments("\"content\"");
        assert_eq!(segments.len(), 1);
        assert_eq!(segments[0].2, "content");

        // Test lines 54-55 - inner loop and closing quote check
        let segments = find_quoted_segments("\"hello world\"");
        assert_eq!(segments.len(), 1);
        assert_eq!(segments[0].2, "hello world");

        // Test line 57 - closing quote found, push segment
        let segments = find_quoted_segments("\"a\" \"b\"");
        assert_eq!(segments.len(), 2);
        assert_eq!(segments[0].2, "a");
        assert_eq!(segments[1].2, "b");

        // Test lines 59-61 - escape character handling
        let segments = find_quoted_segments("\"test\\\\escaped\"");
        assert_eq!(segments.len(), 1);
        assert_eq!(segments[0].2, "test\\\\escaped");
    }

    #[test]
    fn test_find_quoted_segments_escape_scenarios() {
        // Test lines 63-64 - escaped flag reset and character push
        let segments = find_quoted_segments("\"not\\escaped\"");
        assert_eq!(segments.len(), 1);
        assert_eq!(segments[0].2, "not\\escaped");

        // Test line 70 - function return
        let segments = find_quoted_segments("\"test\"");
        assert_eq!(segments, vec![(0, 6, "test".to_string())]);
    }

    #[test]
    fn test_was_token_quoted_detailed() {
        // Test lines 74-75 - function start and find_quoted_segments call
        let result = was_token_quoted("\"test\"", "test");
        assert!(result);

        // Test line 77 - iterate through quoted segments
        let result = was_token_quoted("\"first\" \"second\"", "second");
        assert!(result);

        // Test lines 79-80 - process content with replacements
        let result = was_token_quoted("\"test\\\"quote\"", "test\"quote");
        assert!(!result); // Contains escape sequences

        // Test line 82 - check for escape sequences
        let result = was_token_quoted("\"no\\\\escapes\"", "no\\escapes");
        assert!(!result); // Contains backslash escape

        // Test line 86 - return false when no match
        let result = was_token_quoted("\"hello\"", "world");
        assert!(!result);
    }

    #[test]
    fn test_parse_arguments_detailed() {
        // Test lines 94-96 - function start and initialization
        let result = parse_arguments("");
        assert_eq!(result.args.len(), 0);
        assert_eq!(result.kwargs.len(), 0);

        // Test line 99 - tokenize_with_equals call
        let result = parse_arguments("simple");
        assert_eq!(result.args.len(), 1);

        // Test line 101 - iterate through tokens
        let result = parse_arguments("a b c");
        assert_eq!(result.args.len(), 3);

        // Test lines 103-105 - find equals and split
        let result = parse_arguments("key=value");
        assert_eq!(result.kwargs.len(), 1);
        assert_eq!(result.kwargs[0].0.content, "key");
        assert_eq!(result.kwargs[0].1.content, "value");

        // Test lines 108-109 - check if key and value were quoted
        let result = parse_arguments("\"key\"=\"value\"");
        assert_eq!(result.kwargs.len(), 1);
        assert!(result.kwargs[0].0.quoted);
        assert!(result.kwargs[0].1.quoted);

        // Test lines 112-113 - process content with escapes
        let result = parse_arguments("key=\"test\\\"quote\"");
        assert_eq!(result.kwargs.len(), 1);
        assert_eq!(result.kwargs[0].1.content, "test\"quote"); // Escape processing converts \" to "
    }

    #[test]
    fn test_parse_arguments_special_cases() {
        // Test lines 117-120 - empty key with value starting with '='
        let result = parse_arguments("==value");
        assert_eq!(result.kwargs.len(), 1);
        assert_eq!(result.kwargs[0].0.content, "");
        assert_eq!(result.kwargs[0].1.content, ""); // Special honey-badger recovery

        // Test lines 123-125 - normal kwarg case
        let result = parse_arguments("normalkey=normalvalue");
        assert_eq!(result.kwargs.len(), 1);
        assert_eq!(result.kwargs[0].0.content, "normalkey");
        assert_eq!(result.kwargs[0].1.content, "normalvalue");

        // Test lines 130-132 - positional argument case (no equals)
        let result = parse_arguments("positional");
        assert_eq!(result.args.len(), 1);
        assert_eq!(result.args[0].content, "positional");
    }

    #[test]
    fn test_edge_cases_for_coverage() {
        // Test unclosed quotes in find_quoted_segments
        let segments = find_quoted_segments("\"unclosed");
        assert_eq!(segments.len(), 0); // No properly closed quotes

        // Test empty input scenarios
        let result = process_escapes("");
        assert_eq!(result, "");

        let segments = find_quoted_segments("");
        assert_eq!(segments.len(), 0);

        let result = was_token_quoted("", "");
        assert!(!result);

        // Test complex escape scenarios
        let result = process_escapes("\\\"\\\\\\n");
        assert_eq!(result, "\"\\\\n");

        // Test quotes with only escaped characters
        let segments = find_quoted_segments("\"\\\\\\\"\"");
        assert_eq!(segments.len(), 1);
        assert_eq!(segments[0].2, "\\\\\\\"");

        // Test was_token_quoted with exact escape match but should return false due to escapes
        let result = was_token_quoted("\"test\\\"\"", "test\"");
        assert!(!result);

        // Test multiple equals in value
        let result = parse_arguments("key=value=with=equals");
        assert_eq!(result.kwargs.len(), 1);
        assert_eq!(result.kwargs[0].0.content, "key");
        assert_eq!(result.kwargs[0].1.content, "value=with=equals");
    }

    #[test]
    fn test_honey_badger_recovery() {
        // Test the honey-badger recovery for malformed input
        let result = parse_arguments("=");
        assert_eq!(result.kwargs.len(), 1);
        assert_eq!(result.kwargs[0].0.content, "");
        assert_eq!(result.kwargs[0].1.content, "");

        // Test with actual =value case
        let result = parse_arguments("=actualvalue");
        assert_eq!(result.kwargs.len(), 1);
        assert_eq!(result.kwargs[0].0.content, "");
        assert_eq!(result.kwargs[0].1.content, "actualvalue");

        // Test multiple malformed cases
        let result = parse_arguments("key1= =value2 =");
        assert_eq!(result.args.len(), 0);
        assert_eq!(result.kwargs.len(), 3);
        assert_eq!(result.kwargs[0].0.content, "key1");
        assert_eq!(result.kwargs[0].1.content, "");
        assert_eq!(result.kwargs[1].0.content, "");
        assert_eq!(result.kwargs[1].1.content, "value2");
        assert_eq!(result.kwargs[2].0.content, "");
        assert_eq!(result.kwargs[2].1.content, "");
    }
}
