#![allow(dead_code)]
use crate::parser::Link;
use crate::parser::LABEL_LEN_MAX;
use crate::take_until_unbalanced;
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::character::complete::multispace1;
use nom::combinator::*;
use std::borrow::Cow;
const ESCAPABLE: &str = r#"\'"()[]{}<>"#;
pub fn md_text2dest_link(i: &str) -> nom::IResult<&str, Link> {
    let (i, (te, de, ti)) = md_text2dest(i)?;
    Ok((i, Link::Text2Dest(te, de, ti)))
}
pub fn md_text2dest(i: &str) -> nom::IResult<&str, (Cow<str>, Cow<str>, Cow<str>)> {
    let (i, link_text) = md_link_text(i)?;
    let (i, (link_destination, link_title)) = md_link_destination_enclosed(i)?;
    Ok((i, (link_text, link_destination, link_title)))
}
pub fn md_label2dest_link(i: &str) -> nom::IResult<&str, Link> {
    let (i, (l, d, t)) = md_label2dest(i)?;
    Ok((i, Link::Label2Dest(l, d, t)))
}
pub fn md_label2dest(i: &str) -> nom::IResult<&str, (Cow<str>, Cow<str>, Cow<str>)> {
    
    let (i, _) = nom::bytes::complete::take_while_m_n(0, 3, |c| c == ' ')(i)?;
    
    let (i, link_text) = md_link_label(i)?;
    let (i, _) = nom::character::complete::char(':')(i)?;
    
    let (i, _) = verify(nom::character::complete::multispace1, |s: &str| {
        s.find("\n\n").is_none()
    })(i)?;
    
    let (i, link_destination) = md_link_destination(i)?;
    
    let (i, link_title) = alt((
        
        md_link_title,
        nom::combinator::success(Cow::from("")),
    ))(i)?;
    
    let (i, _) = nom::character::complete::space0(i)?;
    
    if i != "" {
        let _ = nom::character::complete::newline(i)?;
    }
    Ok((i, (link_text, link_destination, link_title)))
}
pub fn md_text2label_link(i: &str) -> nom::IResult<&str, Link> {
    let (i, (t, l)) = md_text2label(i)?;
    Ok((i, Link::Text2Label(t, l)))
}
pub fn md_text2label(i: &str) -> nom::IResult<&str, (Cow<str>, Cow<str>)> {
    let (i, (link_text, link_label)) = alt((
        nom::sequence::pair(md_link_text, md_link_label),
        nom::combinator::map(nom::sequence::terminated(md_link_text, tag("[]")), |s| {
            (s.clone(), s)
        }),
        nom::combinator::map(md_link_text, |s| (s.clone(), s)),
    ))(i)?;
    
    if i != "" {
        let _ = nom::character::complete::none_of("[(")(i)?;
    }
    Ok((i, (link_text, link_label)))
}
fn md_link_text(i: &str) -> nom::IResult<&str, Cow<str>> {
    nom::combinator::map_parser(
        nom::sequence::delimited(tag("["), take_until_unbalanced('[', ']'), tag("]")),
        md_escaped_str_transform,
    )(i)
}
fn md_link_label(i: &str) -> nom::IResult<&str, Cow<str>> {
    nom::combinator::map_parser(
        nom::combinator::verify(
            nom::sequence::delimited(
                tag("["),
                nom::bytes::complete::escaped(
                    nom::character::complete::none_of(r#"\[]"#),
                    '\\',
                    nom::character::complete::one_of(ESCAPABLE),
                ),
                tag("]"),
            ),
            |l: &str| l.len() <= LABEL_LEN_MAX,
        ),
        md_escaped_str_transform,
    )(i)
}
fn md_link_destination(i: &str) -> nom::IResult<&str, Cow<str>> {
    nom::combinator::map_parser(md_parse_link_destination, md_escaped_str_transform)(i)
}
fn md_parse_link_destination(i: &str) -> nom::IResult<&str, &str> {
    alt((
        nom::sequence::delimited(
            tag("<"),
            nom::bytes::complete::escaped(
                nom::character::complete::none_of(r#"\<>"#),
                '\\',
                nom::character::complete::one_of(ESCAPABLE),
            ),
            tag(">"),
        ),
        map_parser(
            nom::bytes::complete::is_not(" \t\r\n"),
            all_consuming(take_until_unbalanced('(', ')')),
        ),
    ))(i)
}
fn md_link_destination_enclosed(i: &str) -> nom::IResult<&str, (Cow<str>, Cow<str>)> {
    let (rest, inner) =
        nom::sequence::delimited(tag("("), take_until_unbalanced('(', ')'), tag(")"))(i)?;
    let (i, link_destination) = md_link_destination(inner)?;
    let (_i, link_title) = alt((
        
        md_link_title,
        nom::combinator::success(Cow::from("")),
    ))(i)?;
    Ok((rest, (link_destination, link_title)))
}
fn md_link_title(i: &str) -> nom::IResult<&str, Cow<str>> {
    nom::combinator::map_parser(md_parse_link_title, md_escaped_str_transform)(i)
}
fn md_parse_link_title(i: &str) -> nom::IResult<&str, &str> {
    nom::sequence::preceded(
        verify(multispace1, |s: &str| s.find("\n\n").is_none()),
        verify(
            alt((
                nom::sequence::delimited(tag("("), take_until_unbalanced('(', ')'), tag(")")),
                nom::sequence::delimited(
                    tag("'"),
                    nom::bytes::complete::escaped(
                        nom::character::complete::none_of(r#"\'"#),
                        '\\',
                        nom::character::complete::one_of(ESCAPABLE),
                    ),
                    tag("'"),
                ),
                nom::sequence::delimited(
                    tag("\""),
                    nom::bytes::complete::escaped(
                        nom::character::complete::none_of(r#"\""#),
                        '\\',
                        nom::character::complete::one_of(ESCAPABLE),
                    ),
                    tag("\""),
                ),
            )),
            |s: &str| s.find("\n\n").is_none(),
        ),
    )(i)
}
fn md_escaped_str_transform(i: &str) -> nom::IResult<&str, Cow<str>> {
    nom::combinator::map(
        nom::bytes::complete::escaped_transform(
            nom::bytes::complete::is_not("\\"),
            '\\',
            nom::character::complete::one_of(ESCAPABLE),
        ),
        |s| if s == i { Cow::from(i) } else { Cow::from(s) },
    )(i)
}
#[cfg(test)]
mod tests {
    use super::*;
    use nom::error::ErrorKind;
    #[test]
    fn test_md_text2dest() {
        assert_eq!(
            md_text2dest("[text](url)abc"),
            Ok(("abc", (Cow::from("text"), Cow::from("url"), Cow::from(""))))
        );
        assert_eq!(
            md_text2dest("[text[i]](url)abc"),
            Ok((
                "abc",
                (Cow::from("text[i]"), Cow::from("url"), Cow::from(""))
            ))
        );
        assert_eq!(
            md_text2dest("[text[i]](ur(l))abc"),
            Ok((
                "abc",
                (Cow::from("text[i]"), Cow::from("ur(l)"), Cow::from(""))
            ))
        );
        assert_eq!(
            md_text2dest("[text(url)"),
            Err(nom::Err::Error(nom::error::Error::new("", ErrorKind::Tag)))
        );
        assert_eq!(
            md_text2dest("[text](<url>)abc"),
            Ok(("abc", (Cow::from("text"), Cow::from("url"), Cow::from(""))))
        );
        assert_eq!(
            md_text2dest("[text](<url> \"link title\")abc"),
            Ok((
                "abc",
                (Cow::from("text"), Cow::from("url"), Cow::from("link title"))
            ))
        );
        assert_eq!(
            md_text2dest("[text](url \"link title\")abc"),
            Ok((
                "abc",
                (Cow::from("text"), Cow::from("url"), Cow::from("link title"))
            ))
        );
    }
    #[test]
    fn test_md_text2label() {
        assert_eq!(
            md_text2label("[link text][link label]abc"),
            Ok(("abc", (Cow::from("link text"), Cow::from("link label"))))
        );
        assert_eq!(
            md_text2label("[link text][]abc"),
            Ok(("abc", (Cow::from("link text"), Cow::from("link text"))))
        );
        assert_eq!(
            md_text2label("[link text]abc"),
            Ok(("abc", (Cow::from("link text"), Cow::from("link text"))))
        );
        assert_eq!(
            md_text2label("[]abc"),
            Ok(("abc", (Cow::from(""), Cow::from(""))))
        );
        assert_eq!(
            md_text2label(""),
            Err(nom::Err::Error(nom::error::Error::new("", ErrorKind::Tag)))
        );
        
        assert_eq!(
            md_text2label("[text]"),
            Ok(("", (Cow::from("text"), Cow::from("text"))))
        );
        assert_eq!(
            md_text2label("[text][text]"),
            Ok(("", (Cow::from("text"), Cow::from("text"))))
        );
        assert_eq!(
            md_text2label("[text][label url"),
            Err(nom::Err::Error(nom::error::Error::new(
                "[label url",
                ErrorKind::NoneOf
            )))
        );
        assert_eq!(
            md_text2label("[text](url)abc"),
            Err(nom::Err::Error(nom::error::Error::new(
                "(url)abc",
                ErrorKind::NoneOf
            )))
        );
    }
    #[test]
    fn test_md_label2dest() {
        assert_eq!(
            md_label2dest("[text]: url\nabc"),
            Ok((
                "\nabc",
                (Cow::from("text"), Cow::from("url"), Cow::from(""))
            ))
        );
        assert_eq!(
            md_label2dest("[text]: url  \nabc"),
            Ok((
                "\nabc",
                (Cow::from("text"), Cow::from("url"), Cow::from(""))
            ))
        );
        assert_eq!(
            md_label2dest("[text]: <url url> \nabc"),
            Ok((
                "\nabc",
                (Cow::from("text"), Cow::from("url url"), Cow::from(""))
            ))
        );
        assert_eq!(
            md_label2dest("[text]: url \"title\"\nabc"),
            Ok((
                "\nabc",
                (Cow::from("text"), Cow::from("url"), Cow::from("title"))
            ))
        );
        assert_eq!(
            md_label2dest("[text]: url\n\"title\"\nabc"),
            Ok((
                "\nabc",
                (Cow::from("text"), Cow::from("url"), Cow::from("title"))
            ))
        );
        assert_eq!(
            md_label2dest("   [text]: url\n\"title\"\nabc"),
            Ok((
                "\nabc",
                (Cow::from("text"), Cow::from("url"), Cow::from("title"))
            ))
        );
        assert_eq!(
            md_label2dest("abc[text]: url\n\"title\""),
            Err(nom::Err::Error(nom::error::Error::new(
                "abc[text]: url\n\"title\"",
                ErrorKind::Tag
            )))
        );
        assert_eq!(
            md_label2dest("    [text]: url\n\"title\" abc"),
            Err(nom::Err::Error(nom::error::Error::new(
                " [text]: url\n\"title\" abc",
                ErrorKind::Tag
            )))
        );
        
        assert_eq!(
            md_label2dest("[text\\[i\\]]: ur(l)url\nabc"),
            Ok((
                "\nabc",
                (Cow::from("text[i]"), Cow::from("ur(l)url"), Cow::from(""))
            ))
        );
        
        assert_eq!(
            md_label2dest("[text[i]]: ur(l)(url"),
            Err(nom::Err::Error(nom::error::Error::new(
                "[i]]: ur(l)(url",
                ErrorKind::Tag
            )))
        );
        
        assert_eq!(
            md_label2dest("[text]: \nurl"),
            Ok(("", (Cow::from("text"), Cow::from("url"), Cow::from(""))))
        );
        
        assert_eq!(
            md_label2dest("[text]: \n\nurl"),
            Err(nom::Err::Error(nom::error::Error::new(
                " \n\nurl",
                ErrorKind::Verify
            )))
        );
        assert_eq!(
            md_label2dest("[text: url"),
            Err(nom::Err::Error(nom::error::Error::new("", ErrorKind::Tag)))
        );
        assert_eq!(
            md_label2dest("[text] url"),
            Err(nom::Err::Error(nom::error::Error::new(
                " url",
                ErrorKind::Char
            )))
        );
        assert_eq!(
            md_label2dest("[text]: url \"link title\"\nabc"),
            Ok((
                "\nabc",
                (Cow::from("text"), Cow::from("url"), Cow::from("link title"))
            ))
        );
        assert_eq!(
            md_label2dest("[text]: url \"link\ntitle\"\nabc"),
            Ok((
                "\nabc",
                (
                    Cow::from("text"),
                    Cow::from("url"),
                    Cow::from("link\ntitle")
                )
            ))
        );
        assert_eq!(
            md_label2dest("[text]: url \"link\ntitle\"abc"),
            Err(nom::Err::Error(nom::error::Error::new(
                "abc",
                ErrorKind::Char
            )))
        );
        assert_eq!(
            md_label2dest("[text]:\nurl \"link\ntitle\"\nabc"),
            Ok((
                "\nabc",
                (
                    Cow::from("text"),
                    Cow::from("url"),
                    Cow::from("link\ntitle")
                )
            ))
        );
        assert_eq!(
            md_label2dest("[text]: url \"link\n\ntitle\"\nabc"),
            Err(nom::Err::Error(nom::error::Error::new(
                "\"link\n\ntitle\"\nabc",
                ErrorKind::Char
            )))
        );
        assert_eq!(
            md_label2dest("[text]:\n\nurl \"link title\"\nabc"),
            Err(nom::Err::Error(nom::error::Error::new(
                "\n\nurl \"link title\"\nabc",
                ErrorKind::Verify
            )))
        );
    }
    #[test]
    fn test_md_link_text() {
        assert_eq!(
            md_link_text("[text](url)"),
            Ok(("(url)", Cow::from("text")))
        );
        assert_eq!(
            md_link_text("[text[i]](url)"),
            Ok(("(url)", Cow::from("text[i]")))
        );
        assert_eq!(
            md_link_text(r#"[text\[i\]](url)"#),
            Ok(("(url)", Cow::from("text[i]")))
        );
        assert_eq!(
            md_link_text("[text(url)"),
            Err(nom::Err::Error(nom::error::Error::new("", ErrorKind::Tag)))
        );
    }
    #[test]
    fn test_md_link_label() {
        assert_eq!(
            md_link_label("[text]: url"),
            Ok((": url", Cow::from("text")))
        );
        assert_eq!(
            md_link_label(r#"[text\[i\]]: url"#),
            Ok((": url", Cow::from("text[i]")))
        );
        assert_eq!(
            md_link_label("[text: url"),
            Err(nom::Err::Error(nom::error::Error::new("", ErrorKind::Tag)))
        );
        assert_eq!(
            md_link_label("[t[ext: url"),
            Err(nom::Err::Error(nom::error::Error::new(
                "[ext: url",
                ErrorKind::Tag
            )))
        );
    }
    #[test]
    fn test_md_link_destination() {
        assert_eq!(
            md_link_destination("url  abc"),
            Ok(("  abc", Cow::from("url")))
        );
        assert_eq!(md_link_destination("url"), Ok(("", Cow::from("url"))));
        assert_eq!(
            md_link_destination("url\nabc"),
            Ok(("\nabc", Cow::from("url")))
        );
        assert_eq!(
            md_link_destination("<url>abc"),
            Ok(("abc", Cow::from("url")))
        );
        assert_eq!(
            md_link_destination(r#"<u\<r\>l>abc"#),
            Ok(("abc", Cow::from(r#"u<r>l"#)))
        );
        assert_eq!(
            md_link_destination(r#"u\)r\(l abc"#),
            Ok((" abc", Cow::from(r#"u)r(l"#)))
        );
        assert_eq!(
            md_link_destination(r#"u(r)l abc"#),
            Ok((" abc", Cow::from(r#"u(r)l"#)))
        );
        assert_eq!(
            md_link_destination("u(r)l\nabc"),
            Ok(("\nabc", Cow::from(r#"u(r)l"#)))
        );
    }
    #[test]
    fn test_md_parse_link_destination() {
        assert_eq!(md_parse_link_destination("<url>abc"), Ok(("abc", "url")));
        assert_eq!(
            md_parse_link_destination(r#"<u\<r\>l>abc"#),
            Ok(("abc", r#"u\<r\>l"#))
        );
        assert_eq!(
            md_link_destination("<url> abc"),
            Ok((" abc", Cow::from("url")))
        );
        assert_eq!(
            md_link_destination("<url>\nabc"),
            Ok(("\nabc", Cow::from("url")))
        );
        assert_eq!(
            md_parse_link_destination("<url 2>abc"),
            Ok(("abc", "url 2"))
        );
        assert_eq!(md_parse_link_destination("url abc"), Ok((" abc", "url")));
        assert_eq!(
            md_parse_link_destination("<url(1)> abc"),
            Ok((" abc", "url(1)"))
        );
        assert_eq!(
            md_parse_link_destination(r#"<[1a]\[1b\](2a)\(2b\)\<3b\>{4a}\{4b\}> abc"#),
            Ok((" abc", r#"[1a]\[1b\](2a)\(2b\)\<3b\>{4a}\{4b\}"#))
        );
        assert_eq!(
            md_parse_link_destination("ur()l abc"),
            Ok((" abc", "ur()l"))
        );
        assert_eq!(
            md_parse_link_destination("ur()l\nabc"),
            Ok(("\nabc", "ur()l"))
        );
    }
    #[test]
    fn test_md_escaped_str_transform() {
        assert_eq!(md_escaped_str_transform(""), Ok(("", Cow::from(""))));
        
        assert_eq!(md_escaped_str_transform("   "), Ok(("", Cow::from("   "))));
        assert_eq!(
            md_escaped_str_transform(r#"abc`:<>abc"#),
            Ok(("", Cow::from(r#"abc`:<>abc"#)))
        );
        assert_eq!(
            md_escaped_str_transform(r#"\<\>\\"#),
            Ok(("", Cow::from(r#"<>\"#)))
        );
        assert_eq!(
            md_escaped_str_transform(r#"\(\)\\"#),
            Ok(("", Cow::from(r#"()\"#)))
        );
    }
    #[test]
    fn test_md_link_title() {
        assert_eq!(
            md_link_title(" (title)abc"),
            Ok(("abc", Cow::from("title")))
        );
        assert_eq!(
            md_link_title(" (ti(t)le)abc"),
            Ok(("abc", Cow::from("ti(t)le")))
        );
        assert_eq!(
            md_link_title(r#" (ti\(t\)le)abc"#),
            Ok(("abc", Cow::from("ti(t)le")))
        );
        assert_eq!(
            md_link_title(r#" "1\\23\"4\'56"abc"#),
            Ok(("abc", Cow::from(r#"1\23"4'56"#)))
        );
        assert_eq!(
            md_link_title(" \"tu\nvwxy\"abc"),
            Ok(("abc", Cow::from("tu\nvwxy")))
        );
        assert_eq!(
            md_link_title(" 'tu\nv\\\'wxy'abc"),
            Ok(("abc", Cow::from("tu\nv\'wxy")))
        );
        assert_eq!(
            md_link_title(" (ti\n\ntle)abc"),
            Err(nom::Err::Error(nom::error::Error::new(
                "(ti\n\ntle)abc",
                ErrorKind::Verify
            )))
        );
    }
    #[test]
    fn test_md_parse_link_title() {
        assert_eq!(md_parse_link_title(" (title)abc"), Ok(("abc", "title")));
        assert_eq!(md_parse_link_title(" (ti(t)le)abc"), Ok(("abc", "ti(t)le")));
        assert_eq!(
            md_parse_link_title(r#" "1\\23\"4\'56"abc"#),
            Ok(("abc", r#"1\\23\"4\'56"#))
        );
        assert_eq!(
            md_parse_link_title(" \"tu\nvwxy\"abc"),
            Ok(("abc", "tu\nvwxy"))
        );
        assert_eq!(
            md_parse_link_title(" 'tu\nv\\\'wxy'abc"),
            Ok(("abc", "tu\nv\\\'wxy"))
        );
        assert_eq!(
            md_parse_link_title(" (ti\n\ntle)abc"),
            Err(nom::Err::Error(nom::error::Error::new(
                "(ti\n\ntle)abc",
                ErrorKind::Verify
            )))
        );
    }
}