//! ValueId Range Allocation for JoinIR Lowering Modules
//!
//! This module manages ValueId ranges for each JoinIR lowering module to prevent
//! ID conflicts when multiple lowerings coexist.
//!
//! ## Current Allocations
//!
//! | Module                  | Range      | Entry  | Loop   | Notes |
//! |-------------------------|------------|--------|--------|-------|
//! | min_loop                | 1000-2999  | 1000+  | 2000+  | Minimal loop test |
//! | skip_ws                 | 3000-4999  | 3000+  | 4000+  | Skip whitespace |
//! | funcscanner_trim        | 5000-6999  | 5000+  | 6000+  | Trim whitespace |
//! | stage1_using_resolver   | 7000-8999  | 7000+  | 8000+  | Stage-1 using resolver |
//! | funcscanner_append_defs | 9000-10999 | 9000+  | 10000+ | FuncScanner append defs |
//! | stageb_body_extract     | 11000-12999| 11000+ | 12000+ | Stage-B body extractor |
//! | stageb_funcscanner      | 13000-14999| 13000+ | 14000+ | Stage-B FuncScanner (scan_all_boxes) |
//!
//! ## Usage Example
//!
//! ```rust,ignore
//! use crate::mir::join_ir::lowering::value_id_ranges::stage1_using_resolver as vid;
//!
//! let entries_param = vid::entry(0);      // ValueId(7000)
//! let n_param = vid::entry(1);            // ValueId(7001)
//! let entries_loop = vid::loop_step(0);   // ValueId(8000)
//! let n_loop = vid::loop_step(1);         // ValueId(8001)
//! ```
//!
//! ## Future Extensions
//!
//! When adding new lowering modules, allocate ranges in increments of 2000:
//! - 9000-10999 (next available)
//! - 11000-12999
//! - 13000-14999
//! - etc.

use crate::mir::ValueId;

/// Base addresses for each lowering module's ValueId range
pub mod base {
    /// min_loop: Minimal loop test (1000-2999)
    #[allow(dead_code)]
    pub const MIN_LOOP: u32 = 1000;

    /// skip_ws: Skip whitespace loop (3000-4999)
    pub const SKIP_WS: u32 = 3000;

    /// funcscanner_trim: Trim whitespace loop (5000-6999)
    pub const FUNCSCANNER_TRIM: u32 = 5000;

    /// stage1_using_resolver: Stage-1 using resolver entries loop (7000-8999)
    pub const STAGE1_USING_RESOLVER: u32 = 7000;

    /// funcscanner_append_defs: FuncScanner append defs loop (9000-10999)
    pub const FUNCSCANNER_APPEND_DEFS: u32 = 9000;

    /// stageb_body_extract: Stage-B body extractor loop (11000-12999)
    pub const STAGEB_BODY_EXTRACT: u32 = 11000;

    /// stageb_funcscanner: Stage-B FuncScanner scan_all_boxes loop (13000-14999)
    pub const STAGEB_FUNCSCANNER: u32 = 13000;
}

/// Helper function to create ValueId from base + offset
///
/// This is a const fn, so it's computed at compile time with zero runtime cost.
#[inline]
pub const fn id(base: u32, offset: u32) -> ValueId {
    ValueId(base + offset)
}

/// ValueId helpers for min_loop lowering module
pub mod min_loop {
    use super::{base, id};
    use crate::mir::ValueId;

    /// Entry function ValueIds (1000-1999)
    #[inline]
    #[allow(dead_code)]
    pub const fn entry(offset: u32) -> ValueId {
        id(base::MIN_LOOP, offset)
    }

    /// Loop function ValueIds (2000-2999)
    #[inline]
    #[allow(dead_code)]
    pub const fn loop_step(offset: u32) -> ValueId {
        id(base::MIN_LOOP, 1000 + offset)
    }
}

/// ValueId helpers for skip_ws lowering module
pub mod skip_ws {
    use super::{base, id};
    use crate::mir::ValueId;

    /// Entry function ValueIds (3000-3999)
    #[inline]
    pub const fn entry(offset: u32) -> ValueId {
        id(base::SKIP_WS, offset)
    }

    /// Loop function ValueIds (4000-4999)
    #[inline]
    pub const fn loop_step(offset: u32) -> ValueId {
        id(base::SKIP_WS, 1000 + offset)
    }
}

/// ValueId helpers for funcscanner_trim lowering module
pub mod funcscanner_trim {
    use super::{base, id};
    use crate::mir::ValueId;

    /// Entry function ValueIds (5000-5999)
    #[inline]
    pub const fn entry(offset: u32) -> ValueId {
        id(base::FUNCSCANNER_TRIM, offset)
    }

    /// Loop function ValueIds (6000-6999)
    #[inline]
    pub const fn loop_step(offset: u32) -> ValueId {
        id(base::FUNCSCANNER_TRIM, 1000 + offset)
    }
}

/// ValueId helpers for stage1_using_resolver lowering module
pub mod stage1_using_resolver {
    use super::{base, id};
    use crate::mir::ValueId;

    /// Entry function ValueIds (7000-7999)
    #[inline]
    pub const fn entry(offset: u32) -> ValueId {
        id(base::STAGE1_USING_RESOLVER, offset)
    }

    /// Loop function ValueIds (8000-8999)
    #[inline]
    pub const fn loop_step(offset: u32) -> ValueId {
        id(base::STAGE1_USING_RESOLVER, 1000 + offset)
    }
}

/// ValueId helpers for funcscanner_append_defs lowering module
pub mod funcscanner_append_defs {
    use super::{base, id};
    use crate::mir::ValueId;

    /// Entry function ValueIds (9000-9999)
    #[inline]
    pub const fn entry(offset: u32) -> ValueId {
        id(base::FUNCSCANNER_APPEND_DEFS, offset)
    }

    /// Loop function ValueIds (10000-10999)
    #[inline]
    pub const fn loop_step(offset: u32) -> ValueId {
        id(base::FUNCSCANNER_APPEND_DEFS, 1000 + offset)
    }
}

/// ValueId helpers for Stage-B body extractor lowering module
pub mod stageb_body_extract {
    use super::{base, id};
    use crate::mir::ValueId;

    /// Entry function ValueIds (11000-11999)
    #[inline]
    pub const fn entry(offset: u32) -> ValueId {
        id(base::STAGEB_BODY_EXTRACT, offset)
    }

    /// Loop function ValueIds (12000-12999)
    #[inline]
    pub const fn loop_step(offset: u32) -> ValueId {
        id(base::STAGEB_BODY_EXTRACT, 1000 + offset)
    }
}

/// ValueId helpers for Stage-B FuncScanner lowering module
pub mod stageb_funcscanner {
    use super::{base, id};
    use crate::mir::ValueId;

    /// Entry function ValueIds (13000-13999)
    #[inline]
    pub const fn entry(offset: u32) -> ValueId {
        id(base::STAGEB_FUNCSCANNER, offset)
    }

    /// Loop function ValueIds (14000-14999)
    #[inline]
    pub const fn loop_step(offset: u32) -> ValueId {
        id(base::STAGEB_FUNCSCANNER, 1000 + offset)
    }
}

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

    /// Macro to test ValueId range boundaries for a lowering module
    ///
    /// Verifies that entry(0) and loop_step(999) produce the expected ValueIds
    /// based on the module's allocated range.
    macro_rules! test_value_id_range {
        ($module:ident, $entry_base:expr, $loop_base:expr) => {
            assert_eq!(
                $module::entry(0).0,
                $entry_base,
                "{} entry(0) should be {}",
                stringify!($module),
                $entry_base
            );
            assert_eq!(
                $module::loop_step(999).0,
                $loop_base + 999,
                "{} loop_step(999) should be {}",
                stringify!($module),
                $loop_base + 999
            );
        };
    }

    #[test]
    fn test_value_id_ranges_no_overlap() {
        // Test each module's range boundaries
        test_value_id_range!(min_loop, 1000, 2000);
        test_value_id_range!(skip_ws, 3000, 4000);
        test_value_id_range!(funcscanner_trim, 5000, 6000);
        test_value_id_range!(stage1_using_resolver, 7000, 8000);
        test_value_id_range!(funcscanner_append_defs, 9000, 10000);
        test_value_id_range!(stageb_body_extract, 11000, 12000);
        test_value_id_range!(stageb_funcscanner, 13000, 14000);

        // Automated overlap detection
        // Each range is 2000 units: (base, base+1999)
        let ranges = vec![
            (1000, 2999),   // min_loop
            (3000, 4999),   // skip_ws
            (5000, 6999),   // funcscanner_trim
            (7000, 8999),   // stage1_using_resolver
            (9000, 10999),  // funcscanner_append_defs
            (11000, 12999), // stageb_body_extract
            (13000, 14999), // stageb_funcscanner
        ];

        // Verify no overlaps between consecutive ranges
        for i in 0..ranges.len() - 1 {
            let (_, end_i) = ranges[i];
            let (start_next, _) = ranges[i + 1];
            assert!(
                end_i < start_next,
                "Overlap detected: range {} ends at {} but range {} starts at {}",
                i,
                end_i,
                i + 1,
                start_next
            );
        }
    }
}
