use pest::iterators::Pairs;
use pest_derive::Parser;
use thiserror::Error;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern "C" {
    #[wasm_bindgen(js_namespace = console)]
    fn log(s: &str);
}
#[derive(Error, Debug)]
pub enum ContextParserError {
    #[error("Failed to parse input: {0}")]
    ParseError(String),
}
#[derive(Parser)]
#[grammar = "context.pest"]
pub struct ContextParser {}
impl ContextParser {
    pub fn parse<'a>(rule: Rule, value: &'a str) -> Result<Pairs<'a, Rule>, ContextParserError> {
        <Self as pest::Parser<Rule>>::parse(rule, value)
            .map_err(|error| ContextParserError::ParseError(format!("{error}")))
    }
}
#[cfg(test)]
mod tests {
    use super::{ContextParser, Rule};
    macro_rules! assert_pair {
        ($pair:expr, $rule:expr, $str:expr) => {
            let pair = $pair;
            assert_eq!(pair.as_rule(), $rule);
            assert_eq!(pair.as_str(), $str);
        };
    }
    #[test]
    fn it_parses_a_basic_paragraph() {
        let mut parsed = ContextParser::parse(Rule::Context, "Greetings, citizen!").unwrap();
        let context = parsed.nth(0).unwrap();
        assert_eq!(context.as_rule(), Rule::Context);
        let mut blocks = context.into_inner();
        let paragraph = blocks.next().unwrap();
        assert_eq!(paragraph.as_rule(), Rule::Paragraph);
        let mut spans = paragraph.into_inner();
        let text = spans.next().unwrap();
        assert_eq!(text.as_rule(), Rule::Text);
    }
    #[test]
    fn it_parses_a_basic_paragraph_with_a_hashtag() {
        let mut parsed =
            ContextParser::parse(Rule::Context, "Greetings, citizen! #copaganda").unwrap();
        let context = parsed.nth(0).unwrap();
        assert_eq!(context.as_rule(), Rule::Context);
        let mut blocks = context.into_inner();
        let paragraph = blocks.next().unwrap();
        assert_eq!(paragraph.as_rule(), Rule::Paragraph);
        let mut spans = paragraph.into_inner();
        let text = spans.next().unwrap();
        assert_eq!(text.as_rule(), Rule::Text);
        assert_eq!(text.as_span().as_str(), "Greetings, citizen! ");
        let hashtag = spans.next().unwrap();
        assert_eq!(hashtag.as_rule(), Rule::HashTag);
        assert_eq!(hashtag.as_span().as_str(), "#copaganda");
    }
    #[test]
    fn it_preserves_boundary_whitespace_in_text_spans() {
        let mut parsed =
            ContextParser::parse(Rule::Context, "Greetings, #citizen! #copaganda").unwrap();
        let context = parsed.nth(0).unwrap();
        assert_eq!(context.as_rule(), Rule::Context);
        let mut blocks = context.into_inner();
        let paragraph = blocks.next().unwrap();
        assert_eq!(paragraph.as_rule(), Rule::Paragraph);
        let mut spans = paragraph.into_inner();
        let text = spans.next().unwrap();
        assert_eq!(text.as_rule(), Rule::Text);
        assert_eq!(text.as_span().as_str(), "Greetings, ");
        let first_hashtag = spans.next().unwrap();
        assert_eq!(first_hashtag.as_rule(), Rule::HashTag);
        assert_eq!(first_hashtag.as_span().as_str(), "#citizen");
        let bridge_text = spans.next().unwrap();
        assert_eq!(bridge_text.as_rule(), Rule::Text);
        assert_eq!(bridge_text.as_span().as_str(), "! ");
        let second_hashtag = spans.next().unwrap();
        assert_eq!(second_hashtag.as_rule(), Rule::HashTag);
        assert_eq!(second_hashtag.as_span().as_str(), "#copaganda");
    }
    #[test]
    fn it_parses_a_basic_flat_list() {
        let mut parsed = ContextParser::parse(
            Rule::Context,
            r#"- foo
- bar
- baz"#,
        )
        .unwrap();
        let context = parsed.nth(0).unwrap();
        assert_eq!(context.as_rule(), Rule::Context);
        let mut blocks = context.into_inner();
        let list = blocks.next().unwrap();
        assert_eq!(list.as_rule(), Rule::List,);
        let mut list_inner = list.into_inner();
        let list_indentation = list_inner.next().unwrap();
        assert_eq!(list_indentation.as_rule(), Rule::ListIndentation);
        let mut list_item_count = 0;
        for item in list_inner {
            list_item_count += 1;
            assert_eq!(item.as_rule(), Rule::ListItem);
        }
        assert_eq!(list_item_count, 3);
    }
    #[test]
    fn it_parses_a_basic_nested_list() {
        let mut parsed = ContextParser::parse(
            Rule::Context,
            r#"- foo
  - bar
- baz"#,
        )
        .unwrap();
        let context = parsed.nth(0).unwrap();
        assert_eq!(context.as_rule(), Rule::Context);
        let mut blocks = context.into_inner();
        let list = blocks.next().unwrap();
        assert_eq!(list.as_rule(), Rule::List);
        let mut outer_list_inner = list.into_inner();
        let list_indentation = outer_list_inner.next().unwrap();
        assert_eq!(list_indentation.as_rule(), Rule::ListIndentation);
        let first_item = outer_list_inner.next().unwrap();
        assert_eq!(first_item.as_rule(), Rule::ListItem);
        let mut first_item_inner = first_item.into_inner();
        let mut item_children = first_item_inner.next().unwrap().into_inner();
        let item_content = item_children.next().unwrap();
        assert_eq!(item_content.as_rule(), Rule::Text);
        let inner_list = first_item_inner.next().unwrap();
        assert_eq!(inner_list.as_rule(), Rule::NestedList);
        let mut inner_list_inner = inner_list.into_inner();
        let list_indentation = inner_list_inner.next().unwrap();
        assert_eq!(list_indentation.as_rule(), Rule::ListIndentation);
        assert_eq!(list_indentation.as_str(), "  ");
        let inner_list_item = inner_list_inner.next().unwrap();
        assert_eq!(inner_list_item.as_rule(), Rule::ListItem);
        let last_item = outer_list_inner.next().unwrap();
        assert_eq!(last_item.as_rule(), Rule::ListItem);
    }
    #[test]
    fn it_parses_a_basic_multiline_quote() {
        let mut parsed = ContextParser::parse(
            Rule::Context,
            r#"> foo
> bar
> baz"#,
        )
        .unwrap();
        let context = parsed.nth(0).unwrap();
        assert_eq!(context.as_rule(), Rule::Context);
        let mut blocks = context.into_inner();
        let quote = blocks.next().unwrap();
        assert_eq!(quote.as_rule(), Rule::Quote);
        let quote_lines = quote.into_inner();
        let mut quote_line_count = 0;
        for quote in quote_lines {
            if quote.as_rule() == Rule::EOI {
                break;
            }
            assert_eq!(quote.as_rule(), Rule::QuoteLine);
            quote_line_count += 1;
        }
        assert_eq!(quote_line_count, 3);
    }
    #[test]
    fn it_parses_a_basic_slashlink() {
        let mut parsed = ContextParser::parse(Rule::Context, "/foo/bar").unwrap();
        let context = parsed.next().unwrap();
        assert_eq!(context.as_rule(), Rule::Context);
        let mut blocks = context.into_inner();
        let paragraph = blocks.next().unwrap();
        assert_eq!(paragraph.as_rule(), Rule::Paragraph);
        let slashlink = paragraph.into_inner().next().unwrap();
        assert_eq!(slashlink.as_rule(), Rule::SlashLink);
        assert_eq!(slashlink.as_span().as_str(), "/foo/bar");
    }
    #[test]
    fn it_supports_extensions_in_slashlinks() {
        let mut parsed = ContextParser::parse(Rule::Context, "/foo/bar.txt").unwrap();
        let context = parsed.next().unwrap();
        assert_eq!(context.as_rule(), Rule::Context);
        let mut blocks = context.into_inner();
        let paragraph = blocks.next().unwrap();
        assert_eq!(paragraph.as_rule(), Rule::Paragraph);
        let slashlink = paragraph.into_inner().next().unwrap();
        assert_eq!(slashlink.as_rule(), Rule::SlashLink);
        assert_eq!(slashlink.as_span().as_str(), "/foo/bar.txt");
        let mut parsed = ContextParser::parse(Rule::Context, "/foo/bar. ").unwrap();
        let context = parsed.next().unwrap();
        assert_eq!(context.as_rule(), Rule::Context);
        let mut blocks = context.into_inner();
        let paragraph = blocks.next().unwrap();
        assert_eq!(paragraph.as_rule(), Rule::Paragraph);
        let slashlink = paragraph.into_inner().next().unwrap();
        assert_eq!(slashlink.as_rule(), Rule::SlashLink);
        assert_eq!(slashlink.as_span().as_str(), "/foo/bar");
    }
    #[test]
    fn it_parses_slashes_preceded_by_printed_characters_as_text() {
        let mut parsed = ContextParser::parse(Rule::Context, "red/black").unwrap();
        let context = parsed.next().unwrap();
        assert_eq!(context.as_rule(), Rule::Context);
        let mut blocks = context.into_inner();
        let paragraph = blocks.next().unwrap();
        assert_eq!(paragraph.as_rule(), Rule::Paragraph);
        let mut spans = paragraph.into_inner();
        let text = spans.next().unwrap();
        assert_eq!(text.as_rule(), Rule::Text);
        assert_eq!(text.as_span().as_str(), "red/black");
        assert!(spans.next().is_none());
    }
    #[test]
    fn it_parses_various_inline_boundary_edge_cases() {
        let mut parsed = ContextParser::parse(
            Rule::Context,
            r#"```foo`
`foo```
foo`bar` `baz`foo
/foo/bar..
foo/bar
foo/bar /foo#baz
/foo foo/bar
@@me
me@example
me @example.
@me@example
@me.@example
##phonenumber
phone#number#
#phone #number
#phone#number
fizz[[buzz]] [[fizz]]
[[fizz]] [[buzz]]fizz
[[[[fizzbuzz]]]]
[[ [[fizzbuzz]]]]
 "#,
        )
        .unwrap();
        let context = parsed.next().unwrap();
        assert_eq!(context.as_rule(), Rule::Context);
        let mut blocks = context.into_inner();
        let paragraph = blocks.next().unwrap();
        assert_eq!(paragraph.as_rule(), Rule::Paragraph);
        let mut spans = paragraph.into_inner();
        assert_pair!(spans.next().unwrap(), Rule::Text, "``");
        assert_pair!(spans.next().unwrap(), Rule::InlineCode, "`foo`");
        let paragraph = blocks.next().unwrap();
        assert_eq!(paragraph.as_rule(), Rule::Paragraph);
        let mut spans = paragraph.into_inner();
        assert_pair!(spans.next().unwrap(), Rule::InlineCode, "`foo`");
        assert_pair!(spans.next().unwrap(), Rule::Text, "``");
        let paragraph = blocks.next().unwrap();
        assert_eq!(paragraph.as_rule(), Rule::Paragraph);
        let mut spans = paragraph.into_inner();
        assert_pair!(spans.next().unwrap(), Rule::Text, "foo`bar` ");
        assert_pair!(spans.next().unwrap(), Rule::InlineCode, "`baz`");
        assert_pair!(spans.next().unwrap(), Rule::Text, "foo");
        blocks.next().unwrap();
        let paragraph = blocks.next().unwrap();
        assert_eq!(paragraph.as_rule(), Rule::Paragraph);
        let mut spans = paragraph.into_inner();
        assert_pair!(spans.next().unwrap(), Rule::SlashLink, "/foo/bar");
        assert_pair!(spans.next().unwrap(), Rule::Text, "..");
        let paragraph = blocks.next().unwrap();
        assert_eq!(paragraph.as_rule(), Rule::Paragraph);
        let mut spans = paragraph.into_inner();
        assert_pair!(spans.next().unwrap(), Rule::Text, "foo/bar");
        let paragraph = blocks.next().unwrap();
        assert_eq!(paragraph.as_rule(), Rule::Paragraph);
        let mut spans = paragraph.into_inner();
        assert_pair!(spans.next().unwrap(), Rule::Text, "foo/bar ");
        assert_pair!(spans.next().unwrap(), Rule::SlashLink, "/foo");
        assert_pair!(spans.next().unwrap(), Rule::HashTag, "#baz");
        let paragraph = blocks.next().unwrap();
        assert_eq!(paragraph.as_rule(), Rule::Paragraph);
        let mut spans = paragraph.into_inner();
        assert_pair!(spans.next().unwrap(), Rule::SlashLink, "/foo");
        assert_pair!(spans.next().unwrap(), Rule::Text, " foo/bar");
        blocks.next().unwrap();
        let paragraph = blocks.next().unwrap();
        assert_eq!(paragraph.as_rule(), Rule::Paragraph);
        let mut spans = paragraph.into_inner();
        assert_pair!(spans.next().unwrap(), Rule::Text, "@@me");
        let paragraph = blocks.next().unwrap();
        assert_eq!(paragraph.as_rule(), Rule::Paragraph);
        let mut spans = paragraph.into_inner();
        assert_pair!(spans.next().unwrap(), Rule::Text, "me@example");
        let paragraph = blocks.next().unwrap();
        assert_eq!(paragraph.as_rule(), Rule::Paragraph);
        let mut spans = paragraph.into_inner();
        assert_pair!(spans.next().unwrap(), Rule::Text, "me ");
        assert_pair!(spans.next().unwrap(), Rule::Mention, "@example");
        assert_pair!(spans.next().unwrap(), Rule::Text, ".");
        let paragraph = blocks.next().unwrap();
        assert_eq!(paragraph.as_rule(), Rule::Paragraph);
        let mut spans = paragraph.into_inner();
        assert_pair!(spans.next().unwrap(), Rule::Mention, "@me");
        assert_pair!(spans.next().unwrap(), Rule::Mention, "@example");
        let paragraph = blocks.next().unwrap();
        assert_eq!(paragraph.as_rule(), Rule::Paragraph);
        let mut spans = paragraph.into_inner();
        assert_pair!(spans.next().unwrap(), Rule::Mention, "@me");
        assert_pair!(spans.next().unwrap(), Rule::Text, ".@example");
        blocks.next().unwrap();
        let paragraph = blocks.next().unwrap();
        assert_eq!(paragraph.as_rule(), Rule::Paragraph);
        let mut spans = paragraph.into_inner();
        assert_pair!(spans.next().unwrap(), Rule::Text, "##phonenumber");
        let paragraph = blocks.next().unwrap();
        assert_eq!(paragraph.as_rule(), Rule::Paragraph);
        let mut spans = paragraph.into_inner();
        assert_pair!(spans.next().unwrap(), Rule::Text, "phone#number#");
        let paragraph = blocks.next().unwrap();
        assert_eq!(paragraph.as_rule(), Rule::Paragraph);
        let mut spans = paragraph.into_inner();
        assert_pair!(spans.next().unwrap(), Rule::HashTag, "#phone");
        assert_pair!(spans.next().unwrap(), Rule::Text, " ");
        assert_pair!(spans.next().unwrap(), Rule::HashTag, "#number");
        let paragraph = blocks.next().unwrap();
        assert_eq!(paragraph.as_rule(), Rule::Paragraph);
        let mut spans = paragraph.into_inner();
        assert_pair!(spans.next().unwrap(), Rule::HashTag, "#phone");
        assert_pair!(spans.next().unwrap(), Rule::HashTag, "#number");
        blocks.next().unwrap();
        let paragraph = blocks.next().unwrap();
        assert_eq!(paragraph.as_rule(), Rule::Paragraph);
        let mut spans = paragraph.into_inner();
        assert_pair!(spans.next().unwrap(), Rule::Text, "fizz[[buzz]] ");
        assert_pair!(spans.next().unwrap(), Rule::WikiLink, "[[fizz]]");
        let paragraph = blocks.next().unwrap();
        assert_eq!(paragraph.as_rule(), Rule::Paragraph);
        let mut spans = paragraph.into_inner();
        assert_pair!(spans.next().unwrap(), Rule::WikiLink, "[[fizz]]");
        assert_pair!(spans.next().unwrap(), Rule::Text, " ");
        assert_pair!(spans.next().unwrap(), Rule::WikiLink, "[[buzz]]");
        assert_pair!(spans.next().unwrap(), Rule::Text, "fizz");
        let paragraph = blocks.next().unwrap();
        assert_eq!(paragraph.as_rule(), Rule::Paragraph);
        let mut spans = paragraph.into_inner();
        assert_pair!(spans.next().unwrap(), Rule::Text, "[[[[fizzbuzz]]]]");
        let paragraph = blocks.next().unwrap();
        assert_eq!(paragraph.as_rule(), Rule::Paragraph);
        let mut spans = paragraph.into_inner();
        assert_pair!(spans.next().unwrap(), Rule::WikiLink, "[[ [[fizzbuzz]]]]");
    }
}