//! Implicit Rails controller routes — emits `@implicit`-tagged Route symbols
//! from controller action method names (`def index`, `def show`, …).
//!
//! Implicit routes are heuristic (not authoritative): they are emitted when
//! no explicit `routes.rb` declaration is found for the controller. The AD6
//! dedup pass in `ckg-cli` removes `@implicit` routes when an
//! `@resources`-tagged route already exists for the same resource.

use tree_sitter::Node;

use crate::lang::ruby::Ctx;
use crate::framework::rails::pluralize_snake;
use super::push_route;

/// Push a route then tag its doc with `@implicit` so consumers can
/// distinguish controller-derived (heuristic) routes from
/// routes.rb-derived (authoritative) ones. The AD6 dedup pass in
/// `ckg-cli` removes `@implicit` routes when an `@resources`-tagged
/// route exists for the same resource.
pub(super) fn push_implicit_route(
    ctx: &mut Ctx<'_>,
    n: Node<'_>,
    method: &str,
    path: &str,
    handler: &str,
) {
    push_route(ctx, n, method, path, handler);
    if let Some(last) = ctx.out.symbols.last_mut() {
        last.doc = format!("@implicit {}", last.doc);
    }
}

/// Emit `@implicit`-tagged REST routes for every standard RESTful action
/// (`index`, `show`, `new`, `create`, `edit`, `update`, `destroy`) found as
/// `def` methods in the controller body.
///
/// Strip namespace (`Admin::PostsController` → `PostsController`) before
/// pluralizing. `pluralize_snake` doesn't understand `::` and would otherwise
/// produce `admin::_posts`, breaking handler matching with routes.rb (which
/// always emits the bare resource name `posts` regardless of
/// `namespace :admin do ... end` — namespace prefixes go on the path, not the
/// handler).
pub(crate) fn emit_implicit_controller_routes(
    ctx: &mut Ctx<'_>,
    body: Node<'_>,
    src: &str,
    class_name: &str,
) {
    let leaf_class = class_name.rsplit("::").next().unwrap_or(class_name).trim();
    let stem = leaf_class.strip_suffix("Controller").unwrap_or(leaf_class);
    let resource = pluralize_snake(stem);
    let mut c = body.walk();
    for stmt in body.children(&mut c) {
        if stmt.kind() != "method" {
            continue;
        }
        let Some(action_name) = crate::lang::ruby::name_of(stmt, src) else {
            continue;
        };
        // Only the standard 7 RESTful actions imply a route. Helper
        // methods, callbacks (`before_action :authenticate_user!`),
        // mailers, and concern-mixed methods all live under `*Controller`
        // and would otherwise inflate route coverage with synthetic
        // routes that don't exist (e.g. `GET /posts/current_user`).
        // Real custom routes belong in `routes.rb` and are extracted
        // separately.
        //
        // Each route emitted here is tagged `@implicit` in its doc
        // field via `push_implicit_route`. When routes.rb declares
        // `resources :posts, only: [:index]`, the routes.rb extractor
        // emits the authoritative `GET /posts` (without `@implicit`),
        // and these controller-derived routes can be filtered out
        // downstream — `kind="route" AND not str_includes(doc,
        // "@implicit")` returns only routes.rb-validated routes.
        // Cross-file intersection here would require a per-repo
        // second pass (routes.rb is a separate file from controllers).
        let (verb, suffix) = match action_name.as_str() {
            "index" => ("GET", String::new()),
            "show" => ("GET", "/:id".into()),
            "new" => ("GET", "/new".into()),
            "create" => ("POST", String::new()),
            "edit" => ("GET", "/:id/edit".into()),
            "update" => ("PATCH", "/:id".into()),
            "destroy" => ("DELETE", "/:id".into()),
            _ => continue,
        };
        let path = format!("/{resource}{suffix}");
        let handler = format!("{resource}#{action_name}");
        push_implicit_route(ctx, stmt, verb, &path, &handler);
    }
}
