//! Directive splitter for master/spec.md.
//!
//! Recognises three directives, one per line, no nesting:
//!   {form-only: json} … {/form-only}
//!   {form-only: ail} … {/form-only}
//!   {example: <id>}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Form {
    Json,
    Ail,
}

impl Form {
    fn parse(name: &str) -> Option<Form> {
        match name {
            "json" => Some(Form::Json),
            "ail" => Some(Form::Ail),
            _ => None,
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Segment {
    Prose(String),
    FormOnly { form: Form, body: String },
    Example(String),
}

/// Split a master/spec.md text into a sequence of segments.
///
/// Panics on malformed input (unknown form name, unclosed form-only block).
/// The renderer treats these as authoring bugs that should fail the test
/// suite rather than silently produce diverging mini-specs.
pub fn split(input: &str) -> Vec<Segment> {
    let mut out: Vec<Segment> = Vec::new();
    let mut current_prose = String::new();
    let mut current_block: Option<(Form, String)> = None;

    for line in input.split_inclusive('\n') {
        let trimmed = line.trim_end_matches('\n');

        // Inside a form-only block: collect until the closing marker.
        if let Some((form, body)) = current_block.as_mut() {
            if trimmed == "{/form-only}" {
                let body_owned = std::mem::take(body);
                out.push(Segment::FormOnly { form: *form, body: body_owned });
                current_block = None;
            } else {
                body.push_str(line);
            }
            continue;
        }

        // {form-only: <name>}
        if let Some(rest) = trimmed.strip_prefix("{form-only: ").and_then(|r| r.strip_suffix("}")) {
            flush_prose(&mut current_prose, &mut out);
            let form = Form::parse(rest)
                .unwrap_or_else(|| panic!("unknown form name in directive: {}", rest));
            current_block = Some((form, String::new()));
            continue;
        }

        // {example: <id>}
        if let Some(id) = trimmed.strip_prefix("{example: ").and_then(|r| r.strip_suffix("}")) {
            flush_prose(&mut current_prose, &mut out);
            out.push(Segment::Example(id.to_string()));
            continue;
        }

        // Stray closer with no opener.
        if trimmed == "{/form-only}" {
            panic!("stray {{/form-only}} without matching opener");
        }

        // Plain prose line.
        current_prose.push_str(line);
    }

    if current_block.is_some() {
        panic!("unclosed form-only block at end of input");
    }
    flush_prose(&mut current_prose, &mut out);
    out
}

fn flush_prose(buf: &mut String, out: &mut Vec<Segment>) {
    if !buf.is_empty() {
        out.push(Segment::Prose(std::mem::take(buf)));
    }
}
