use mail_parser::MessageParser;

// Duplicate the word-joiner here since pub(crate) is not visible from integration tests.
fn parse_encoded_word(s: &str) -> Option<(&str, &str, &str, &str)> {
    let s = s.strip_prefix("=?")?;
    let (charset, rest) = s.split_once('?')?;
    let (encoding, rest) = rest.split_once('?')?;
    if encoding.len() != 1 {
        return None;
    }
    let enc_char = encoding.chars().next()?.to_ascii_uppercase();
    if enc_char != 'B' && enc_char != 'Q' {
        return None;
    }
    let end = rest.find("?=")?;
    let payload = &rest[..end];
    let remaining = &rest[end + 2..];
    Some((charset, encoding, payload, remaining))
}

fn join_adjacent_encoded_words(s: &str) -> String {
    let mut out = String::with_capacity(s.len());
    let mut pos = 0;
    while pos < s.len() {
        let remaining = &s[pos..];
        if !remaining.starts_with("=?") {
            let ch = remaining.chars().next().unwrap();
            out.push(ch);
            pos += ch.len_utf8();
            continue;
        }
        match parse_encoded_word(remaining) {
            None => {
                out.push('=');
                pos += 1;
            }
            Some((charset, encoding, payload, after_first)) => {
                let mut combined = payload.to_string();
                let mut cursor = after_first;
                loop {
                    let ws = cursor.trim_start_matches(|c: char| c == ' ' || c == '\t');
                    if !ws.starts_with("=?") {
                        break;
                    }
                    match parse_encoded_word(ws) {
                        None => break,
                        Some((cs2, enc2, payload2, after_next)) => {
                            if cs2.eq_ignore_ascii_case(charset)
                                && enc2.eq_ignore_ascii_case(encoding)
                            {
                                combined.push_str(payload2);
                                cursor = after_next;
                            } else {
                                break;
                            }
                        }
                    }
                }
                out.push_str("=?");
                out.push_str(charset);
                out.push('?');
                out.push_str(encoding);
                out.push('?');
                out.push_str(&combined);
                out.push_str("?=");
                pos += remaining.len() - cursor.len();
            }
        }
    }
    out
}

fn preprocess(bytes: &[u8]) -> Vec<u8> {
    match std::str::from_utf8(bytes) {
        Ok(s) => join_adjacent_encoded_words(s).into_bytes(),
        Err(_) => bytes.to_vec(),
    }
}

fn parse_subject(raw: &[u8]) -> String {
    let preprocessed = preprocess(raw);
    MessageParser::default()
        .parse(&preprocessed)
        .and_then(|m| m.subject().map(|s| s.to_string()))
        .unwrap_or_default()
}

/// ЕИРЦ = D0 95  D0 98  D0 A0  D0 A6
/// Full base64: 0JXQmNCg0KY=
#[test]
fn verify_full_base64() {
    let raw = b"Subject: =?UTF-8?B?0JXQmNCg0KY=?=\n\nBody";
    assert_eq!(
        parse_subject(raw),
        "ЕИРЦ",
        "Full base64 sanity check failed"
    );
}

/// Plain UTF-8 – always worked.
#[test]
fn test_raw_utf8() {
    let raw = "Subject: ЕИРЦ\n\nBody".as_bytes();
    assert_eq!(parse_subject(raw), "ЕИРЦ");
}

/// UTF-8 Quoted-Printable – already worked before fix.
#[test]
fn test_utf8_qp() {
    let raw = b"Subject: =?UTF-8?Q?=D0=95=D0=98=D0=A0=D0=A6?=\n\nBody";
    assert_eq!(parse_subject(raw), "ЕИРЦ");
}

/// Multi-byte char split across two adjacent words (no whitespace).
/// Split: first 6 bytes (D0 95 D0 98 D0 A0) → 0JXQmNCg
///        last  2 bytes (D0 A6)              → 0KY=
/// mail-parser sees И split across the boundary.
#[test]
fn test_split_utf8_encoded_words() {
    let raw = b"Subject: =?UTF-8?B?0JXQmNCg?==?UTF-8?B?0KY=?=\n\nBody";
    assert_eq!(
        parse_subject(raw),
        "ЕИРЦ",
        "Split encoded-word (no space) failed"
    );
}

/// Same split with linear whitespace (RFC 2047 §6.2).
#[test]
fn test_split_utf8_with_whitespace() {
    let raw = b"Subject: =?UTF-8?B?0JXQmNCg?= =?UTF-8?B?0KY=?=\n\nBody";
    assert_eq!(parse_subject(raw), "ЕИРЦ", "Split with whitespace failed");
}

/// Split across multiple lines (CRLF + space), simulating folded headers.
#[test]
fn test_split_utf8_folded_header() {
    let raw = b"Subject: =?UTF-8?B?0JXQmNCg?=\r\n =?UTF-8?B?0KY=?=\n\nBody";
    assert_eq!(parse_subject(raw), "ЕИРЦ", "Split folded header failed");
}
