// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
//
// ASCII supervision tree diagram generator.
// Produces a visual representation of the OTP supervision hierarchy
// showing supervisors, workers, strategies, and restart policies.

use crate::manifest::{Manifest, to_module_name};

/// Generate an ASCII supervision tree diagram from the manifest.
/// Shows the complete hierarchy with strategies and restart types.
pub fn generate_tree_diagram(manifest: &Manifest) -> String {
    let root_module = extract_root_module(&manifest.workload.entry);
    let mut lines = Vec::new();

    lines.push(format!(
        "Supervision Tree: {} (otpiser)",
        manifest.workload.name
    ));
    lines.push("=".repeat(60));
    lines.push(String::new());
    lines.push(format!(
        "{}.Application [strategy: {}]",
        root_module, manifest.workload.strategy
    ));

    if manifest.supervisors.is_empty() {
        lines.push(format!("  +-- {}.Supervisor [one_for_one]", root_module));
        lines.push("      (no children defined)".to_string());
    } else {
        let sup_count = manifest.supervisors.len();
        for (i, sup) in manifest.supervisors.iter().enumerate() {
            let is_last_sup = i == sup_count - 1;
            let _connector = if is_last_sup { "`" } else { "|" };
            let branch = if is_last_sup { "`" } else { "|" };
            let sup_module_name = to_module_name(&sup.name);
            lines.push(format!(
                "  {}-- {}.{}Supervisor [strategy: {}, restarts: {}/{}s]",
                branch,
                root_module,
                sup_module_name,
                sup.strategy,
                sup.max_restarts,
                sup.max_seconds
            ));

            let child_count = sup.children.len();
            for (j, child) in sup.children.iter().enumerate() {
                let is_last_child = j == child_count - 1;
                let child_prefix = if is_last_sup { "   " } else { "  |" };
                let child_branch = if is_last_child { "`" } else { "|" };

                let module_name = child
                    .module
                    .as_deref()
                    .map(String::from)
                    .unwrap_or_else(|| to_module_name(&child.name));

                let type_label = if child.child_type == "supervisor" {
                    "supervisor"
                } else {
                    "worker"
                };

                lines.push(format!(
                    "  {} {}-- {}.{} [{}] restart: {}",
                    child_prefix, child_branch, root_module, module_name, type_label, child.restart
                ));
            }

            // Add a blank line between supervisors (but not after the last one).
            if !is_last_sup {
                lines.push("  |".to_string());
            }
        }
    }

    lines.push(String::new());
    lines.push("Legend:".to_string());
    lines.push(
        "  [strategy: X]   — Supervision strategy (one_for_one | one_for_all | rest_for_one)"
            .to_string(),
    );
    lines.push("  [worker]        — GenServer process".to_string());
    lines.push("  [supervisor]    — Nested supervisor".to_string());
    lines.push("  restart: X      — permanent | transient | temporary".to_string());
    lines.push(String::new());
    lines.push("Generated by otpiser — https://github.com/hyperpolymath/otpiser".to_string());

    lines.join("\n")
}

/// Extract root module name from entry (same logic as elixir module).
fn extract_root_module(entry: &str) -> &str {
    if let Some(pos) = entry.rfind(".Application") {
        &entry[..pos]
    } else if let Some(pos) = entry.rfind('.') {
        &entry[..pos]
    } else {
        entry
    }
}
