/// RFC 3501 modified UTF-7 decoder for IMAP mailbox names
///
/// Modified UTF-7 (RFC 3501) encoding rules:
/// - Printable US-ASCII characters (0x20-0x7e, except "&") are represented by themselves
/// - "&" is represented by "&-"
/// - All other characters are represented as "&" + modified-BASE64(UTF-16BE) + "-"
/// - Modified BASE64 uses "A-Za-z0-9,+" instead of "A-Za-z0-9+/"

pub fn decode_modified_utf7(encoded: &str) -> String {
    let bytes = encoded.as_bytes();
    let mut result = String::new();
    let mut i = 0;

    while i < bytes.len() {
        let byte = bytes[i];

        // Printable ASCII (except '&') - pass through
        if byte != b'&' && byte >= 0x20 && byte <= 0x7e {
            result.push(byte as char);
            i += 1;
            continue;
        }

        // "&" starts a sequence
        if byte == b'&' {
            if i + 1 < bytes.len() && bytes[i + 1] == b'-' {
                // "&-" represents literal "&"
                result.push('&');
                i += 2;
                continue;
            }

            // Find the closing "-"
            let mut j = i + 1;
            while j < bytes.len() && bytes[j] != b'-' {
                j += 1;
            }

            if j >= bytes.len() {
                // Malformed: no closing "-", treat as literal
                result.push('&');
                i += 1;
                continue;
            }

            // Extract the modified-BASE64 sequence
            let b64_str = std::str::from_utf8(&bytes[i + 1..j]).unwrap_or("");

            // Decode modified-BASE64 (replace "," with "/")
            let b64_std = b64_str.replace(',', "/");

            // Decode from standard BASE64
            if let Ok(decoded_bytes) = base64_decode(&b64_std) {
                // Convert UTF-16BE bytes to UTF-16 chars, then to UTF-8
                if let Ok(text) = decode_utf16be(&decoded_bytes) {
                    result.push_str(&text);
                } else {
                    // Invalid UTF-16, use replacement character
                    result.push('\u{FFFD}');
                }
            } else {
                // Invalid BASE64, use replacement character
                result.push('\u{FFFD}');
            }

            i = j + 1;
            continue;
        }

        // Other bytes (shouldn't happen in valid UTF-7)
        result.push('\u{FFFD}');
        i += 1;
    }

    result
}

/// Decode standard BASE64 (RFC 4648)
fn base64_decode(input: &str) -> Result<Vec<u8>, String> {
    const TABLE: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    let mut result = Vec::new();
    let input = input.as_bytes();
    let mut i = 0;

    while i < input.len() {
        let mut buf = [0u8; 4];
        let mut len = 0;

        // Read up to 4 base64 characters
        while len < 4 && i < input.len() {
            let b = input[i];
            if b == b'=' {
                break;
            }
            if let Some(pos) = TABLE.iter().position(|&x| x == b) {
                buf[len] = pos as u8;
                len += 1;
            } else if !b.is_ascii_whitespace() {
                return Err("Invalid base64 character".to_string());
            }
            i += 1;
        }

        // Decode the 4-character group
        match len {
            0 => break,
            1 => return Err("Invalid base64 length".to_string()),
            2 => {
                result.push(((buf[0] << 2) | (buf[1] >> 4)) as u8);
            }
            3 => {
                result.push(((buf[0] << 2) | (buf[1] >> 4)) as u8);
                result.push((((buf[1] & 0x0f) << 4) | (buf[2] >> 2)) as u8);
            }
            4 => {
                result.push(((buf[0] << 2) | (buf[1] >> 4)) as u8);
                result.push((((buf[1] & 0x0f) << 4) | (buf[2] >> 2)) as u8);
                result.push((((buf[2] & 0x03) << 6) | buf[3]) as u8);
            }
            _ => return Err("Invalid base64 length".to_string()),
        }

        // Skip padding
        while i < input.len() && input[i] == b'=' {
            i += 1;
        }
    }

    Ok(result)
}

/// Decode UTF-16BE bytes to UTF-8 string
fn decode_utf16be(bytes: &[u8]) -> Result<String, String> {
    if bytes.len() % 2 != 0 {
        return Err("Odd number of bytes in UTF-16BE sequence".to_string());
    }

    let mut chars = Vec::new();
    let mut i = 0;

    while i < bytes.len() {
        let high = u16::from_be_bytes([bytes[i], bytes[i + 1]]);
        i += 2;

        // Check for surrogate pair
        if (high & 0xFC00) == 0xD800 {
            // High surrogate, expect low surrogate
            if i + 2 > bytes.len() {
                chars.push('\u{FFFD}');
                break;
            }
            let low = u16::from_be_bytes([bytes[i], bytes[i + 1]]);
            i += 2;

            if (low & 0xFC00) != 0xDC00 {
                chars.push('\u{FFFD}');
                continue;
            }

            // Decode surrogate pair
            let high_bits = (high & 0x3FF) as u32;
            let low_bits = (low & 0x3FF) as u32;
            let codepoint = 0x10000 + (high_bits << 10) + low_bits;

            if let Some(ch) = char::from_u32(codepoint) {
                chars.push(ch);
            } else {
                chars.push('\u{FFFD}');
            }
        } else if (high & 0xFC00) == 0xDC00 {
            // Unexpected low surrogate
            chars.push('\u{FFFD}');
        } else {
            // Regular BMP character
            if let Some(ch) = char::from_u32(high as u32) {
                chars.push(ch);
            } else {
                chars.push('\u{FFFD}');
            }
        }
    }

    Ok(chars.iter().collect())
}

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

    #[test]
    fn test_ascii_passthrough() {
        assert_eq!(decode_modified_utf7("INBOX"), "INBOX");
        assert_eq!(decode_modified_utf7("Sent Mail"), "Sent Mail");
        assert_eq!(decode_modified_utf7("Drafts"), "Drafts");
    }

    #[test]
    fn test_literal_ampersand() {
        assert_eq!(decode_modified_utf7("A&-B"), "A&B");
        assert_eq!(decode_modified_utf7("&-"), "&");
    }

    #[test]
    fn test_mixed_ascii_and_encoded() {
        // Test that ASCII parts pass through while encoded parts are decoded
        // This is a basic test to ensure the structure works
        assert!(decode_modified_utf7("Folder&-Name").contains("&"));
    }

    #[test]
    fn test_decode_empty() {
        assert_eq!(decode_modified_utf7(""), "");
    }
}
