//! Documentation generation for Shuck configuration options.

use std::fmt::Write;

use shuck_config::{ConfigFieldMetadata, ConfigSectionMetadata, configuration_metadata};

pub fn generate_settings_reference() -> String {
    let mut output = String::new();
    output.push_str("# Settings Reference\n\n");
    output.push_str("{/* This file is generated by `pnpm generate:config`. */}\n\n");
    output.push_str(
        "This page is generated from Shuck's Rust configuration metadata and lists the supported `shuck.toml` and `.shuck.toml` settings.\n\n",
    );
    output.push_str(
        "Use [Configuration](/docs/configuration) for discovery, precedence, and examples of how config files are resolved.\n\n",
    );

    for section in configuration_metadata() {
        emit_section(&mut output, section, &mut Vec::new());
    }

    output
}

fn emit_section(output: &mut String, section: &ConfigSectionMetadata, parents: &mut Vec<&str>) {
    parents.push(section.key);
    let path = parents.join(".");
    let heading = if parents.len() == 1 { "##" } else { "###" };

    let _ = writeln!(output, "{heading} `{}`\n", section_heading(&path));
    output.push_str(section.docs);
    output.push_str("\n\n");

    for field in section.fields {
        emit_field(output, field, &path);
    }

    for nested in section.sections {
        emit_section(output, nested, parents);
    }

    parents.pop();
}

fn emit_field(output: &mut String, field: &ConfigFieldMetadata, section_path: &str) {
    let _ = writeln!(output, "#### `{}`\n", field.key);
    output.push_str(field.docs);
    output.push_str("\n\n");
    let _ = writeln!(output, "**Default value**: `{}`\n", field.default);
    let _ = writeln!(output, "**Type**: `{}`\n", field.value_type);
    output.push_str("**Example usage**:\n\n");
    output.push_str("```toml\n");
    emit_example_headers(output, section_path);
    output.push_str(field.example);
    output.push_str("\n```\n\n");
    output.push_str("---\n\n");
}

fn section_heading(path: &str) -> String {
    if path == "lint.contracts.custom" {
        format!("[[{path}]]")
    } else {
        format!("[{path}]")
    }
}

fn emit_example_headers(output: &mut String, section_path: &str) {
    if section_path == "lint.contracts.custom" {
        let _ = writeln!(output, "[[lint.contracts.custom]]");
    } else if let Some(nested) = section_path.strip_prefix("lint.contracts.custom.") {
        let _ = writeln!(output, "[[lint.contracts.custom]]");
        let _ = writeln!(output, "[lint.contracts.custom.{nested}]");
    } else {
        let _ = writeln!(output, "[{section_path}]");
    }
}

#[cfg(test)]
mod tests {
    use super::generate_settings_reference;

    #[test]
    fn generated_reference_includes_known_sections() {
        let reference = generate_settings_reference();

        assert!(reference.contains("## `[check]`"));
        assert!(reference.contains("## `[format]`"));
        assert!(reference.contains("## `[lint]`"));
        assert!(reference.contains("### `[lint.rule-options.c001]`"));
        assert!(reference.contains("### `[[lint.contracts.custom]]`"));
        assert!(reference.contains("[[lint.contracts.custom]]\nid ="));
        assert!(
            reference
                .contains("[[lint.contracts.custom]]\n[lint.contracts.custom.consumes]\nnames =")
        );
        assert!(reference.contains("#### `embedded`"));
        assert!(reference.contains("#### `indent-style`"));
    }
}
