use nom::{
    IResult, Parser,
    branch::alt,
    character::complete::{char, line_ending, space0},
    combinator::{not, peek, value},
    multi::{many_m_n, separated_list0},
    sequence::preceded,
};

use crate::{
    ast::Inline,
    parser::util::{line_terminated, not_eof_or_eol1},
};

pub(super) fn paragraph<'a>(
    check_first_line: bool,
) -> impl FnMut(&'a str) -> IResult<&'a str, Vec<Inline>> {
    move |input: &'a str| {
        let mut lines = Vec::new();
        let input = if check_first_line {
            input
        } else {
            // Skip checks for the first line, just make it a paragraph
            let (input, first_line) =
                preceded(many_m_n(0, 3, char(' ')), not_eof_or_eol1).parse(input)?;
            lines.push(first_line);
            input
        };

        let paragraph_parser = separated_list0(
            line_ending,
            preceded(
                is_paragraph_line_start(),
                preceded(many_m_n(0, 3, char(' ')), not_eof_or_eol1),
            ),
        );
        let (input, rest_lines) = line_terminated(paragraph_parser).parse(input)?;
        lines.extend(rest_lines);

        let content = lines.join("\n");

        let (_, content) = crate::parser::inline::inline_many1()
            .parse(content.as_str())
            .map_err(|err| err.map_input(|_| input))?;

        Ok((input, content))
    }
}

pub(super) fn is_paragraph_line_start<'a>() -> impl FnMut(&'a str) -> IResult<&'a str, ()> {
    move |input: &'a str| {
        peek(not(alt((
            value((), crate::parser::blocks::heading::heading_v1()),
            value((), crate::parser::blocks::heading::heading_v2_level()),
            crate::parser::blocks::thematic_break::thematic_break(),
            value((), crate::parser::blocks::blockquote::blockquote()),
            value((), crate::parser::blocks::list::list_item()),
            value((), crate::parser::blocks::code_block::code_block_fenced()),
            value((), crate::parser::blocks::html_block::html_block()),
            value(
                (),
                crate::parser::blocks::link_definition::link_definition(),
            ),
            value(
                (),
                crate::parser::blocks::footnote_definition::footnote_definition(),
            ),
            value((), crate::parser::blocks::table::table()),
            value((), line_terminated(space0)),
        ))))
        .parse(input)
    }
}
