use derive_more::Display;
use std::collections::BTreeMap;
use std::error::Error;
use std::io::Write;
use std::num::ParseIntError;
use std::{fmt, io, str};
use thiserror::Error;

/// Bencoded element.
#[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Clone)]
pub enum Element {
    Integer(i64),
    ByteString(Vec<u8>),
    List(Vec<Element>),
    Dictionary(BTreeMap<Element, Element>),
}

impl Element {
    /// Parse bencoded bytes.
    pub fn from_bytes(src: &[u8]) -> Result<Element, ParseError> {
        match read_element(src) {
            Ok((entity, _)) => Ok(entity),
            Err(e) => Err(e),
        }
    }

    /// Parse bencoded bytes. Returns the produced [`Element`] and the number of parsed bytes.
    pub fn from_bytes_with_len(src: &[u8]) -> Result<(Element, usize), ParseError> {
        let (entity, rest) = read_element(src)?;
        Ok((entity, src.len() - rest.len()))
    }

    /// Serialize to a vec of bytes.
    pub fn encode(&self) -> Vec<u8> {
        let mut dest = Vec::<u8>::new();
        write_element(self, &mut dest);
        dest
    }
}

impl From<&str> for Element {
    fn from(text: &str) -> Self {
        Element::ByteString(Vec::<u8>::from(text))
    }
}

impl From<String> for Element {
    fn from(value: String) -> Self {
        Element::ByteString(value.into_bytes())
    }
}

impl From<i64> for Element {
    fn from(number: i64) -> Self {
        Element::Integer(number)
    }
}

impl fmt::Display for Element {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        const INDENT_INCREMENT: usize = 3;
        fn write_entity(
            entity: &Element,
            f: &mut fmt::Formatter<'_>,
            last_indent: usize,
            ignore_first_indent: bool,
        ) -> fmt::Result {
            let first_indent = if ignore_first_indent { 0 } else { last_indent };
            match entity {
                Element::ByteString(data) => match std::str::from_utf8(data) {
                    Ok(text) => write!(f, "{:first_indent$}\"{}\"", ' ', text)?,
                    Err(_) => write!(f, "{:first_indent$}<{} raw bytes>", ' ', data.len())?,
                },
                Element::Integer(number) => write!(f, "{:first_indent$}{}", ' ', number)?,
                Element::List(elements) => {
                    writeln!(f, "{:first_indent$}[", ' ')?;
                    for e in elements {
                        write_entity(e, f, last_indent + INDENT_INCREMENT, false)?;
                        writeln!(f, ",")?;
                    }
                    write!(f, "{:last_indent$}]", ' ')?;
                }
                Element::Dictionary(map) => {
                    writeln!(f, "{:first_indent$}{{", ' ')?;
                    for (key, val) in map {
                        write_entity(key, f, last_indent + INDENT_INCREMENT, false)?;
                        write!(f, ":")?;
                        write_entity(val, f, last_indent + INDENT_INCREMENT, true)?;
                        writeln!(f, ",")?;
                    }
                    write!(f, "{:last_indent$}}}", ' ')?;
                }
            }
            Ok(())
        }
        write_entity(self, f, INDENT_INCREMENT, false)
    }
}

/// Errors that can occur during parsing of bencoded data.
#[derive(Debug, Error, Display)]
pub enum ParseError {
    EmptySource,
    #[display("invalid prefix ({_0})")]
    InvalidPrefix(u8),
    NoIntegerPrefix,
    NoIntegerEnd,
    NoStringDelimeter,
    #[display("invalid string length ({_0})")]
    InvalidStringLength(usize),
    NoListPrefix,
    NoDictionaryPrefix,
    #[display("{_0}")]
    ExternalError(#[source] Box<dyn Error + Send + Sync>),
}

impl From<ParseIntError> for ParseError {
    fn from(e: ParseIntError) -> Self {
        ParseError::ExternalError(Box::new(e))
    }
}

impl From<str::Utf8Error> for ParseError {
    fn from(e: str::Utf8Error) -> Self {
        ParseError::ExternalError(Box::new(e))
    }
}

impl From<ParseError> for io::Error {
    fn from(e: ParseError) -> Self {
        io::Error::other(e)
    }
}

/// Utility function for converting a bencoded dictionary to a more user-friendly representation.
pub fn convert_dictionary(src: BTreeMap<Element, Element>) -> BTreeMap<String, Element> {
    fn to_string_key((key, value): (Element, Element)) -> Option<(String, Element)> {
        if let Element::ByteString(data) = key
            && let Ok(text) = String::from_utf8(data)
        {
            Some((text, value))
        } else {
            None
        }
    }
    src.into_iter().filter_map(to_string_key).collect()
}

const DELIMITER_STRING: u8 = b':';
const PREFIX_INTEGER: u8 = b'i';
const PREFIX_LIST: u8 = b'l';
const PREFIX_DICTIONARY: u8 = b'd';
const SUFFIX_COMMON: u8 = b'e';

fn write_element(e: &Element, dest: &mut Vec<u8>) {
    match e {
        Element::Integer(number) => {
            let _ = write!(dest, "{}{}{}", PREFIX_INTEGER as char, number, SUFFIX_COMMON as char);
        }
        Element::ByteString(data) => {
            let _ = write!(dest, "{}{}", data.len(), DELIMITER_STRING as char);
            dest.extend_from_slice(data);
        }
        Element::List(list) => {
            dest.push(PREFIX_LIST);
            for e in list {
                write_element(e, dest);
            }
            dest.push(SUFFIX_COMMON);
        }
        Element::Dictionary(map) => {
            dest.push(PREFIX_DICTIONARY);
            for (key, value) in map {
                write_element(key, dest);
                write_element(value, dest);
            }
            dest.push(SUFFIX_COMMON);
        }
    };
}

fn read_element(src: &[u8]) -> Result<(Element, &[u8]), ParseError> {
    let first_byte = src.first().ok_or(ParseError::EmptySource)?;
    match *first_byte {
        b'0'..=b'9' => read_string(src),
        PREFIX_INTEGER => read_integer(src),
        PREFIX_LIST => read_list(src),
        PREFIX_DICTIONARY => read_dictionary(src),
        prefix => Err(ParseError::InvalidPrefix(prefix)),
    }
}

fn split_once(src: &[u8], delimeter: u8) -> Option<(&[u8], &[u8])> {
    let opt_index = src.iter().position(|b| *b == delimeter);
    match opt_index {
        Some(index) => {
            let lhs = unsafe { src.get_unchecked(..index) };
            let rhs = unsafe { src.get_unchecked(index + 1..) };
            Some((lhs, rhs))
        }
        None => None,
    }
}

fn read_integer(src: &[u8]) -> Result<(Element, &[u8]), ParseError> {
    let rest = src.strip_prefix(&[PREFIX_INTEGER]).ok_or(ParseError::NoIntegerPrefix)?;

    let (number_data, rest) = split_once(rest, SUFFIX_COMMON).ok_or(ParseError::NoIntegerEnd)?;
    let number_text = str::from_utf8(number_data)?;
    let number = number_text.parse::<i64>()?;

    Ok((Element::Integer(number), rest))
}

fn read_string(src: &[u8]) -> Result<(Element, &[u8]), ParseError> {
    let (size_data, rest) =
        split_once(src, DELIMITER_STRING).ok_or(ParseError::NoStringDelimeter)?;
    let size_text = str::from_utf8(size_data)?;
    let size = size_text.parse::<usize>()?;

    let data = rest.get(..size).ok_or(ParseError::InvalidStringLength(size))?;
    let rest = unsafe { rest.get_unchecked(size..) };

    Ok((Element::ByteString(Vec::from(data)), rest))
}

fn read_list(src: &[u8]) -> Result<(Element, &[u8]), ParseError> {
    let mut rest = src.strip_prefix(&[PREFIX_LIST]).ok_or(ParseError::NoListPrefix)?;

    let mut list = Vec::new();
    while !rest.starts_with(&[SUFFIX_COMMON]) {
        let (entity, new_rest) = read_element(rest)?;
        list.push(entity);
        rest = new_rest;
    }
    Ok((Element::List(list), unsafe { rest.get_unchecked(1..) }))
}

fn read_dictionary(src: &[u8]) -> Result<(Element, &[u8]), ParseError> {
    let mut rest = src.strip_prefix(&[PREFIX_DICTIONARY]).ok_or(ParseError::NoDictionaryPrefix)?;

    let mut map = BTreeMap::new();
    while !rest.starts_with(&[SUFFIX_COMMON]) {
        let (key, new_rest) = read_string(rest)?;
        rest = new_rest;

        let (value, new_rest) = read_element(rest)?;
        rest = new_rest;

        map.insert(key, value);
    }
    Ok((Element::Dictionary(map), unsafe { rest.get_unchecked(1..) }))
}

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

    #[test]
    fn test_decode_and_encode_negative_integer() {
        let input = b"i-42e";

        let parsed = read_integer(input);
        assert!(parsed.is_ok(), "Error: {:?}", parsed.err());

        let (entity, rest) = parsed.unwrap();
        assert_eq!(Element::Integer(-42), entity);
        assert!(rest.is_empty());

        assert_eq!(input, entity.encode().as_slice());
    }

    #[test]
    fn test_decode_invalid_integer() {
        let input = b"i0_0e";

        let parsed = read_integer(input);
        assert!(parsed.is_err());

        if let ParseError::ExternalError(msg) = parsed.err().unwrap() {
            assert_eq!("invalid digit found in string", format!("{msg}"));
        } else {
            panic!("Wrong error type");
        }
    }

    #[test]
    fn test_decode_non_utf8_integer() {
        let input = [PREFIX_INTEGER, 200, 201, 202, SUFFIX_COMMON];

        let parsed = read_integer(&input);
        assert!(parsed.is_err());

        if let ParseError::ExternalError(msg) = parsed.err().unwrap() {
            assert_eq!("invalid utf-8 sequence of 1 bytes from index 0", format!("{msg}"));
        } else {
            panic!("Wrong error type");
        }
    }

    #[test]
    fn test_decode_and_encode_simple_string() {
        let input = b"15:A simple string";

        let parsed = read_string(input);
        assert!(parsed.is_ok(), "Error: {:?}", parsed.err());

        let (entity, rest) = parsed.unwrap();
        assert_eq!(Element::ByteString(Vec::from(b"A simple string".as_slice())), entity);
        assert!(rest.is_empty());

        assert_eq!(input, entity.encode().as_slice());
    }

    #[test]
    fn test_decode_and_encode_non_ascii_string() {
        let input = "22:Добрый день!";

        let parsed = read_string(input.as_bytes());
        assert!(parsed.is_ok(), "Error: {:?}", parsed.err());

        let (entity, rest) = parsed.unwrap();
        assert_eq!(Element::ByteString(Vec::from("Добрый день!".as_bytes())), entity);
        assert!(rest.is_empty());

        assert_eq!(input.as_bytes(), entity.encode().as_slice());
    }

    #[test]
    fn test_decode_and_encode_binary_string() {
        let input = [b'4', b':', 0xf1, 0xf2, 0xf3, 0xf4];

        let parsed = read_string(&input);
        assert!(parsed.is_ok(), "Error: {:?}", parsed.err());

        let (entity, rest) = parsed.unwrap();
        assert_eq!(Element::ByteString(Vec::from([0xf1, 0xf2, 0xf3, 0xf4].as_slice())), entity);
        assert!(rest.is_empty());

        assert_eq!(&input, entity.encode().as_slice());
    }

    #[test]
    fn test_decode_string_with_invalid_length() {
        let input = [b'9', b'2', b':', 0xf1, 0xf2, 0xf3, 0xf4];

        let parsed = read_string(&input);
        assert!(parsed.is_err());
        assert!(matches!(parsed.err().unwrap(), ParseError::InvalidStringLength(_)));
    }

    #[test]
    fn test_decode_and_encode_simple_list() {
        let input = "li-42ei42e15:A simple stringe";

        let parsed = read_list(input.as_bytes());
        assert!(parsed.is_ok(), "Error: {:?}", parsed.err());

        let (entity, rest) = parsed.unwrap();
        assert!(rest.is_empty());

        if let Element::List(ref list) = entity {
            assert_eq!(3, list.len());
            assert_eq!(Element::Integer(-42), *list.get(0).unwrap());
            assert_eq!(Element::Integer(42), *list.get(1).unwrap());
            assert_eq!(
                Element::ByteString(Vec::from("A simple string".as_bytes())),
                *list.get(2).unwrap()
            );
        } else {
            panic!("Not a list");
        }

        assert_eq!(input.as_bytes(), entity.encode().as_slice());
    }

    #[test]
    fn test_decode_list_without_end_postfix() {
        let input = "li-42ei42e15:A simple string";

        let parsed = read_list(input.as_bytes());
        assert!(parsed.is_err());
        assert!(matches!(parsed.err().unwrap(), ParseError::EmptySource));
    }

    #[test]
    fn test_decode_and_encode_list_with_binary_string() {
        let input = {
            let start = b"li42e";
            let end = [b'4', b':', 0xf1, 0xf2, 0xf3, 0xf4, b'e'];
            let mut buf = Vec::<u8>::new();
            buf.write_all(start).unwrap();
            buf.write_all(&end).unwrap();
            buf
        };

        let parsed = read_list(&input);
        assert!(parsed.is_ok(), "Error: {:?}", parsed.err());

        let (entity, rest) = parsed.unwrap();
        assert!(rest.is_empty());

        if let Element::List(ref list) = entity {
            assert_eq!(2, list.len());
            assert_eq!(Element::Integer(42), *list.get(0).unwrap());
            assert_eq!(
                Element::ByteString(Vec::from([0xf1, 0xf2, 0xf3, 0xf4].as_slice())),
                *list.get(1).unwrap()
            );
        } else {
            panic!("Not a list");
        }

        assert_eq!(&input, entity.encode().as_slice());
    }

    #[test]
    fn test_decode_and_encode_simple_dictionary() {
        let input = "d8:announce41:http://tracker.trackerfix.com:80/announcee";

        let parsed = read_dictionary(input.as_bytes());
        assert!(parsed.is_ok(), "Error: {:?}", parsed.err());

        let (entity, rest) = parsed.unwrap();
        assert!(rest.is_empty());
        if let Element::Dictionary(ref map) = entity {
            assert_eq!(1, map.len());
            let (key, value) = map.iter().next().unwrap();
            assert_eq!(Element::ByteString(Vec::from("announce".as_bytes())), *key);
            assert_eq!(
                Element::ByteString(Vec::from(
                    "http://tracker.trackerfix.com:80/announce".as_bytes()
                )),
                *value
            );
        } else {
            panic!("Not a dictionary");
        }

        assert_eq!(input.as_bytes(), entity.encode().as_slice());
    }

    #[test]
    fn test_decode_dictionary_with_appended_data() {
        let input = "d8:msg_typei1e5:piecei0e10:total_sizei34256eexxxxxxxx";

        let parsed = Element::from_bytes(input.as_bytes()).unwrap();

        if let Element::Dictionary(map) = parsed {
            let mut it = map.into_iter();

            let (key, value) = it.next().unwrap();
            assert_eq!(Element::from("msg_type"), key);
            assert_eq!(Element::Integer(1), value);

            let (key, value) = it.next().unwrap();
            assert_eq!(Element::from("piece"), key);
            assert_eq!(Element::Integer(0), value);

            let (key, value) = it.next().unwrap();
            assert_eq!(Element::from("total_size"), key);
            assert_eq!(Element::Integer(34256), value);

            assert!(it.next().is_none());
        } else {
            panic!("Not a dictionary");
        }
    }

    #[test]
    fn test_decode_and_encode_real_dictionary() {
        let input = "d8:announce41:http://tracker.trackerfix.com:80/announce13:announce-listll41:http://tracker.trackerfix.com:80/announceel30:udp://9.rarbg.me:2720/announceel30:udp://9.rarbg.to:2740/announceel42:udp://tracker.fatkhoala.org:13780/announceel44:udp://tracker.tallpenguin.org:15760/announceee7:comment40:Torrent downloaded from https://rarbg.to10:created by5:RARBG13:creation datei1629718368e4:infod5:filesld6:lengthi30e4:pathl9:RARBG.txteed6:lengthi99e4:pathl23:RARBG_DO_NOT_MIRROR.exeeed6:lengthi66667e4:pathl4:Subs13:10_French.srteed6:lengthi67729e4:pathl4:Subs13:11_German.srteed6:lengthi98430e4:pathl4:Subs12:12_Greek.srteed6:lengthi89001e4:pathl4:Subs13:13_Hebrew.srteed6:lengthi66729e4:pathl4:Subs10:14_hrv.srteed6:lengthi69251e4:pathl4:Subs16:15_Hungarian.srteed6:lengthi67897e4:pathl4:Subs17:16_Indonesian.srteed6:lengthi67035e4:pathl4:Subs14:17_Italian.srteed6:lengthi68310e4:pathl4:Subs15:18_Japanese.srteed6:lengthi79479e4:pathl4:Subs13:19_Korean.srteed6:lengthi67367e4:pathl4:Subs10:20_may.srteed6:lengthi63337e4:pathl4:Subs13:21_Bokmal.srteed6:lengthi68715e4:pathl4:Subs13:22_Polish.srteed6:lengthi67838e4:pathl4:Subs17:23_Portuguese.srteed6:lengthi69077e4:pathl4:Subs17:24_Portuguese.srteed6:lengthi70967e4:pathl4:Subs15:25_Romanian.srteed6:lengthi90311e4:pathl4:Subs14:26_Russian.srteed6:lengthi67143e4:pathl4:Subs14:27_Spanish.srteed6:lengthi67068e4:pathl4:Subs14:28_Spanish.srteed6:lengthi63229e4:pathl4:Subs14:29_Swedish.srteed6:lengthi97509e4:pathl4:Subs12:2_Arabic.srteed6:lengthi126859e4:pathl4:Subs11:30_Thai.srteed6:lengthi69519e4:pathl4:Subs14:31_Turkish.srteed6:lengthi87216e4:pathl4:Subs10:32_ukr.srteed6:lengthi86745e4:pathl4:Subs17:33_Vietnamese.srteed6:lengthi71908e4:pathl4:Subs13:3_Chinese.srteed6:lengthi71949e4:pathl4:Subs13:4_Chinese.srteed6:lengthi69054e4:pathl4:Subs11:5_Czech.srteed6:lengthi64987e4:pathl4:Subs12:6_Danish.srteed6:lengthi60512e4:pathl4:Subs11:7_Dutch.srteed6:lengthi81102e4:pathl4:Subs13:8_English.srteed6:lengthi62658e4:pathl4:Subs13:9_Finnish.srteed6:lengthi1397575464e4:pathl66:The.Witcher.Nightmare.of.the.Wolf.2021.1080p.WEBRip.x265-RARBG.mp4eee4:name62:The.Witcher.Nightmare.of.the.Wolf.2021.1080p.WEBRip.x265-RARBG12:piece lengthi2097152eee";

        let parsed = read_dictionary(input.as_bytes());
        assert!(parsed.is_ok(), "Error: {:?}", parsed.err());

        let entity = parsed.unwrap();
        match entity.0 {
            Element::Dictionary(_) => (),
            _ => panic!(),
        };

        assert_eq!(input.as_bytes(), entity.0.encode().as_slice());
    }

    #[test]
    fn test_format_bencode() {
        #[rustfmt::skip]
        let expected = 
r#"   {
      "announce": "http://tracker.trackerfix.com:80/announce",
      "announce-list": [
         [
            "http://tracker.trackerfix.com:80/announce",
         ],
         [
            "udp://9.rarbg.me:2720/announce",
         ],
         [
            "udp://9.rarbg.to:2740/announce",
         ],
         [
            "udp://tracker.fatkhoala.org:13780/announce",
         ],
         [
            "udp://tracker.tallpenguin.org:15760/announce",
         ],
      ],
      "comment": "Torrent downloaded from https://rarbg.to",
      "created by": "RARBG",
      "creation date": 1629718368,
      "info": {
         "files": [
            {
               "length": 30,
               "path": [
                  "RARBG.txt",
               ],
            },
            {
               "length": 99,
               "path": [
                  "RARBG_DO_NOT_MIRROR.exe",
               ],
            },
            {
               "length": 66667,
               "path": [
                  "Subs",
                  "10_French.srt",
               ],
            },
            {
               "length": 67729,
               "path": [
                  "Subs",
                  "11_German.srt",
               ],
            },
            {
               "length": 98430,
               "path": [
                  "Subs",
                  "12_Greek.srt",
               ],
            },
            {
               "length": 89001,
               "path": [
                  "Subs",
                  "13_Hebrew.srt",
               ],
            },
            {
               "length": 66729,
               "path": [
                  "Subs",
                  "14_hrv.srt",
               ],
            },
            {
               "length": 69251,
               "path": [
                  "Subs",
                  "15_Hungarian.srt",
               ],
            },
            {
               "length": 67897,
               "path": [
                  "Subs",
                  "16_Indonesian.srt",
               ],
            },
            {
               "length": 67035,
               "path": [
                  "Subs",
                  "17_Italian.srt",
               ],
            },
            {
               "length": 68310,
               "path": [
                  "Subs",
                  "18_Japanese.srt",
               ],
            },
            {
               "length": 79479,
               "path": [
                  "Subs",
                  "19_Korean.srt",
               ],
            },
            {
               "length": 67367,
               "path": [
                  "Subs",
                  "20_may.srt",
               ],
            },
            {
               "length": 63337,
               "path": [
                  "Subs",
                  "21_Bokmal.srt",
               ],
            },
            {
               "length": 68715,
               "path": [
                  "Subs",
                  "22_Polish.srt",
               ],
            },
            {
               "length": 67838,
               "path": [
                  "Subs",
                  "23_Portuguese.srt",
               ],
            },
            {
               "length": 69077,
               "path": [
                  "Subs",
                  "24_Portuguese.srt",
               ],
            },
            {
               "length": 70967,
               "path": [
                  "Subs",
                  "25_Romanian.srt",
               ],
            },
            {
               "length": 90311,
               "path": [
                  "Subs",
                  "26_Russian.srt",
               ],
            },
            {
               "length": 67143,
               "path": [
                  "Subs",
                  "27_Spanish.srt",
               ],
            },
            {
               "length": 67068,
               "path": [
                  "Subs",
                  "28_Spanish.srt",
               ],
            },
            {
               "length": 63229,
               "path": [
                  "Subs",
                  "29_Swedish.srt",
               ],
            },
            {
               "length": 97509,
               "path": [
                  "Subs",
                  "2_Arabic.srt",
               ],
            },
            {
               "length": 126859,
               "path": [
                  "Subs",
                  "30_Thai.srt",
               ],
            },
            {
               "length": 69519,
               "path": [
                  "Subs",
                  "31_Turkish.srt",
               ],
            },
            {
               "length": 87216,
               "path": [
                  "Subs",
                  "32_ukr.srt",
               ],
            },
            {
               "length": 86745,
               "path": [
                  "Subs",
                  "33_Vietnamese.srt",
               ],
            },
            {
               "length": 71908,
               "path": [
                  "Subs",
                  "3_Chinese.srt",
               ],
            },
            {
               "length": 71949,
               "path": [
                  "Subs",
                  "4_Chinese.srt",
               ],
            },
            {
               "length": 69054,
               "path": [
                  "Subs",
                  "5_Czech.srt",
               ],
            },
            {
               "length": 64987,
               "path": [
                  "Subs",
                  "6_Danish.srt",
               ],
            },
            {
               "length": 60512,
               "path": [
                  "Subs",
                  "7_Dutch.srt",
               ],
            },
            {
               "length": 81102,
               "path": [
                  "Subs",
                  "8_English.srt",
               ],
            },
            {
               "length": 62658,
               "path": [
                  "Subs",
                  "9_Finnish.srt",
               ],
            },
            {
               "length": 1397575464,
               "path": [
                  "The.Witcher.Nightmare.of.the.Wolf.2021.1080p.WEBRip.x265-RARBG.mp4",
               ],
            },
         ],
         "name": "The.Witcher.Nightmare.of.the.Wolf.2021.1080p.WEBRip.x265-RARBG",
         "piece length": 2097152,
      },
   }"#;

        let input = "d8:announce41:http://tracker.trackerfix.com:80/announce13:announce-listll41:http://tracker.trackerfix.com:80/announceel30:udp://9.rarbg.me:2720/announceel30:udp://9.rarbg.to:2740/announceel42:udp://tracker.fatkhoala.org:13780/announceel44:udp://tracker.tallpenguin.org:15760/announceee7:comment40:Torrent downloaded from https://rarbg.to10:created by5:RARBG13:creation datei1629718368e4:infod5:filesld6:lengthi30e4:pathl9:RARBG.txteed6:lengthi99e4:pathl23:RARBG_DO_NOT_MIRROR.exeeed6:lengthi66667e4:pathl4:Subs13:10_French.srteed6:lengthi67729e4:pathl4:Subs13:11_German.srteed6:lengthi98430e4:pathl4:Subs12:12_Greek.srteed6:lengthi89001e4:pathl4:Subs13:13_Hebrew.srteed6:lengthi66729e4:pathl4:Subs10:14_hrv.srteed6:lengthi69251e4:pathl4:Subs16:15_Hungarian.srteed6:lengthi67897e4:pathl4:Subs17:16_Indonesian.srteed6:lengthi67035e4:pathl4:Subs14:17_Italian.srteed6:lengthi68310e4:pathl4:Subs15:18_Japanese.srteed6:lengthi79479e4:pathl4:Subs13:19_Korean.srteed6:lengthi67367e4:pathl4:Subs10:20_may.srteed6:lengthi63337e4:pathl4:Subs13:21_Bokmal.srteed6:lengthi68715e4:pathl4:Subs13:22_Polish.srteed6:lengthi67838e4:pathl4:Subs17:23_Portuguese.srteed6:lengthi69077e4:pathl4:Subs17:24_Portuguese.srteed6:lengthi70967e4:pathl4:Subs15:25_Romanian.srteed6:lengthi90311e4:pathl4:Subs14:26_Russian.srteed6:lengthi67143e4:pathl4:Subs14:27_Spanish.srteed6:lengthi67068e4:pathl4:Subs14:28_Spanish.srteed6:lengthi63229e4:pathl4:Subs14:29_Swedish.srteed6:lengthi97509e4:pathl4:Subs12:2_Arabic.srteed6:lengthi126859e4:pathl4:Subs11:30_Thai.srteed6:lengthi69519e4:pathl4:Subs14:31_Turkish.srteed6:lengthi87216e4:pathl4:Subs10:32_ukr.srteed6:lengthi86745e4:pathl4:Subs17:33_Vietnamese.srteed6:lengthi71908e4:pathl4:Subs13:3_Chinese.srteed6:lengthi71949e4:pathl4:Subs13:4_Chinese.srteed6:lengthi69054e4:pathl4:Subs11:5_Czech.srteed6:lengthi64987e4:pathl4:Subs12:6_Danish.srteed6:lengthi60512e4:pathl4:Subs11:7_Dutch.srteed6:lengthi81102e4:pathl4:Subs13:8_English.srteed6:lengthi62658e4:pathl4:Subs13:9_Finnish.srteed6:lengthi1397575464e4:pathl66:The.Witcher.Nightmare.of.the.Wolf.2021.1080p.WEBRip.x265-RARBG.mp4eee4:name62:The.Witcher.Nightmare.of.the.Wolf.2021.1080p.WEBRip.x265-RARBG12:piece lengthi2097152eee";

        let parsed = read_dictionary(input.as_bytes());
        assert!(parsed.is_ok(), "Error: {:?}", parsed.err());

        let entity = parsed.unwrap().0;
        assert_eq!(expected, format!("{entity}"), "\n{entity}\n");
    }
}
