#![allow(dead_code)]
use crate::take_until_unbalanced;
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::combinator::*;
use nom::error::ErrorKind;
use std::borrow::Cow;
pub fn md_link(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,
        (
            Cow::Borrowed(link_text),
            link_destination,
            Cow::Borrowed(link_title),
        ),
    ))
}
pub fn md_link_ref(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_ref_text(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)?;
    if let Ok((i, _)) = verify(
        nom::character::complete::multispace1::<_, (_, ErrorKind)>,
        |s: &str| s.find("\n\n").is_none(),
    )(i)
    {
        let (i, link_title) = verify(md_link_title, |s: &str| s.find("\n\n").is_none())(i)?;
        Ok((
            i,
            (
                Cow::Borrowed(link_text),
                link_destination,
                Cow::Borrowed(link_title),
            ),
        ))
    } else {
        Ok((
            i,
            (
                Cow::Borrowed(link_text),
                link_destination,
                Cow::Borrowed(""),
            ),
        ))
    }
}
fn md_link_text(i: &str) -> nom::IResult<&str, &str> {
    nom::sequence::delimited(tag("["), take_until_unbalanced('[', ']'), tag("]"))(i)
}
fn md_link_ref_text(i: &str) -> nom::IResult<&str, &str> {
    nom::sequence::delimited(tag("["), take_until_unbalanced('[', ']'), tag("]:"))(i)
}
fn md_link_destination(i: &str) -> nom::IResult<&str, Cow<str>> {
    nom::combinator::map_parser(
        md_parse_link_destination,
        md_escaped_link_destination_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(r#"<>"#),
            ),
            tag(">"),
        ),
        map_parser(
            nom::bytes::complete::is_not(" \t\r\n"),
            all_consuming(take_until_unbalanced('(', ')')),
        ),
    ))(i)
}
fn md_escaped_link_destination_transform(i: &str) -> nom::IResult<&str, Cow<str>> {
    nom::combinator::map(
        nom::bytes::complete::escaped_transform(
            nom::bytes::complete::is_not("\\"),
            '\\',
            alt((
                value("\\", tag("\\")),
                value("<", tag("<")),
                value(">", tag(">")),
            )),
        ),
        |s| if s == i { Cow::from(i) } else { Cow::from(s) },
    )(i)
}
fn md_link_destination_enclosed(i: &str) -> nom::IResult<&str, (Cow<str>, &str)> {
    let (rest, inner) =
        nom::sequence::delimited(tag("("), take_until_unbalanced('(', ')'), tag(")"))(i)?;
    let (i, link_destination) = md_link_destination(inner)?;
    if let Ok((i, _)) = nom::character::complete::multispace1::<_, (_, ErrorKind)>(i) {
        let (_, link_title) = md_link_title(i)?;
        Ok((rest, (link_destination, link_title)))
    } else {
        Ok((rest, (link_destination, "")))
    }
}
fn md_link_title(i: &str) -> nom::IResult<&str, &str> {
    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(r#"n"'()"#),
                ),
                tag("'"),
            ),
            nom::sequence::delimited(
                tag("\""),
                nom::bytes::complete::escaped(
                    nom::character::complete::none_of(r#"\""#),
                    '\\',
                    nom::character::complete::one_of(r#"n"'()"#),
                ),
                tag("\""),
            ),
        )),
        |s: &str| s.find("\n\n").is_none(),
    )(i)
}
#[cfg(test)]
mod tests {
    use super::*;
    use nom::error::ErrorKind;
    #[test]
    fn test_md_link() {
        assert_eq!(
            md_link("[text](url)abc"),
            Ok(("abc", (Cow::from("text"), Cow::from("url"), Cow::from(""))))
        );
        assert_eq!(
            md_link("[text[i]](url)abc"),
            Ok((
                "abc",
                (Cow::from("text[i]"), Cow::from("url"), Cow::from(""))
            ))
        );
        assert_eq!(
            md_link("[text[i]](ur(l))abc"),
            Ok((
                "abc",
                (Cow::from("text[i]"), Cow::from("ur(l)"), Cow::from(""))
            ))
        );
        assert_eq!(
            md_link("[text(url)"),
            Err(nom::Err::Error(nom::error::Error::new("", ErrorKind::Tag)))
        );
        assert_eq!(
            md_link("[text](<url>)abc"),
            Ok(("abc", (Cow::from("text"), Cow::from("url"), Cow::from(""))))
        );
        assert_eq!(
            md_link("[text](<url> \"link title\")abc"),
            Ok((
                "abc",
                (Cow::from("text"), Cow::from("url"), Cow::from("link title"))
            ))
        );
        assert_eq!(
            md_link("[text](url \"link title\")abc"),
            Ok((
                "abc",
                (Cow::from("text"), Cow::from("url"), Cow::from("link title"))
            ))
        );
    }
    #[test]
    fn test_md_link_ref() {
        assert_eq!(
            md_link_ref("[text]: url\n\"abc\""),
            Ok(("", (Cow::from("text"), Cow::from("url"), Cow::from("abc"))))
        );
        assert_eq!(
            md_link_ref("   [text]: url\n\"abc\""),
            Ok(("", (Cow::from("text"), Cow::from("url"), Cow::from("abc"))))
        );
        assert_eq!(
            md_link_ref("abc[text]: url\n\"abc\""),
            Err(nom::Err::Error(nom::error::Error::new(
                "abc[text]: url\n\"abc\"",
                ErrorKind::Tag
            )))
        );
        assert_eq!(
            md_link_ref("    [text]: url\n\"abc\""),
            Err(nom::Err::Error(nom::error::Error::new(
                " [text]: url\n\"abc\"",
                ErrorKind::Tag
            )))
        );
        
        assert_eq!(
            md_link_ref("[text[i]]: ur(l)url"),
            Ok((
                "",
                (Cow::from("text[i]"), Cow::from("ur(l)url"), Cow::from(""))
            ))
        );
        
        assert_eq!(
            md_link_ref("[text[i]]: ur(l)(url"),
            Err(nom::Err::Error(nom::error::Error::new(
                "ur(l)(url",
                ErrorKind::TakeUntil
            )))
        );
        
        assert_eq!(
            md_link_ref("[text]: \nurl"),
            Ok(("", (Cow::from("text"), Cow::from("url"), Cow::from(""))))
        );
        
        assert_eq!(
            md_link_ref("[text]: \n\nurl"),
            Err(nom::Err::Error(nom::error::Error::new(
                " \n\nurl",
                ErrorKind::Verify
            )))
        );
        assert_eq!(
            md_link_ref("[text: url"),
            Err(nom::Err::Error(nom::error::Error::new("", ErrorKind::Tag)))
        );
        assert_eq!(
            md_link_ref("[text] url"),
            Err(nom::Err::Error(nom::error::Error::new(
                "] url",
                ErrorKind::Tag
            )))
        );
        assert_eq!(
            md_link_ref("[text]: url \"link title\"\nabc"),
            Ok((
                "\nabc",
                (Cow::from("text"), Cow::from("url"), Cow::from("link title"))
            ))
        );
        assert_eq!(
            md_link_ref("[text]: url \"link\ntitle\"\nabc"),
            Ok((
                "\nabc",
                (
                    Cow::from("text"),
                    Cow::from("url"),
                    Cow::from("link\ntitle")
                )
            ))
        );
        assert_eq!(
            md_link_ref("[text]: url \"link\n\ntitle\"\nabc"),
            Err(nom::Err::Error(nom::error::Error::new(
                "\"link\n\ntitle\"\nabc",
                ErrorKind::Verify
            )))
        );
        assert_eq!(
            md_link_ref("[text]:\nurl \"link\ntitle\"\nabc"),
            Ok((
                "\nabc",
                (
                    Cow::from("text"),
                    Cow::from("url"),
                    Cow::from("link\ntitle")
                )
            ))
        );
        assert_eq!(
            md_link_ref("[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)", "text")));
        assert_eq!(md_link_text("[text[i]](url)"), Ok(("(url)", "text[i]")));
        assert_eq!(
            md_link_text("[text(url)"),
            Err(nom::Err::Error(nom::error::Error::new("", ErrorKind::Tag)))
        );
    }
    #[test]
    fn test_md_link_ref_text() {
        assert_eq!(md_link_ref_text("[text]: url"), Ok((" url", "text")));
        assert_eq!(md_link_ref_text("[text[i]]: url"), Ok((" url", "text[i]")));
        assert_eq!(
            md_link_ref_text("[text: url"),
            Err(nom::Err::Error(nom::error::Error::new("", ErrorKind::Tag)))
        );
        assert_eq!(
            md_link_ref_text("[t[ext: url"),
            Err(nom::Err::Error(nom::error::Error::new(
                "t[ext: url",
                ErrorKind::TakeUntil
            )))
        );
    }
    #[test]
    fn test_md_link_destination() {
        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"#)))
        );
    }
    #[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_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("ur()l abc"),
            Ok((" abc", "ur()l"))
        );
        assert_eq!(
            md_link_title("<url>>abc"),
            Err(nom::Err::Error(nom::error::Error::new(
                "<url>>abc",
                ErrorKind::Tag
            )))
        );
        assert_eq!(
            md_link_title("<u<rl>abc"),
            Err(nom::Err::Error(nom::error::Error::new(
                "<u<rl>abc",
                ErrorKind::Tag
            )))
        );
    }
    #[test]
    fn test_md_escaped_link_destination_transform() {
        assert_eq!(
            md_escaped_link_destination_transform(""),
            Ok(("", Cow::from("")))
        );
        
        assert_eq!(
            md_escaped_link_destination_transform("   "),
            Ok(("", Cow::from("   ")))
        );
        assert_eq!(
            md_escaped_link_destination_transform(r#"abc`:<>abc"#),
            Ok(("", Cow::from(r#"abc`:<>abc"#)))
        );
        assert_eq!(
            md_escaped_link_destination_transform(r#"\<\>\\"#),
            Ok(("", Cow::from(r#"<>\"#)))
        );
    }
    #[test]
    fn test_md_link_title() {
        assert_eq!(md_link_title("(title)abc"), Ok(("abc", "title")));
        assert_eq!(md_link_title("(ti(t)le)abc"), Ok(("abc", "ti(t)le")));
        assert_eq!(
            md_link_title(r#""123\"456"abc"#),
            Ok(("abc", r#"123\"456"#))
        );
        assert_eq!(
            md_link_title(r#""tu\nv\"wxy"abc"#),
            Ok(("abc", r#"tu\nv\"wxy"#))
        );
        assert_eq!(
            md_link_title(r#"'tu\nv\'wxy'abc"#),
            Ok(("abc", r#"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
            )))
        );
    }
}