//! Phase 33-17: Tail Call Classification
//!
//! Classifies tail calls in JoinIR loops into three semantic categories.
//!
//! Extracted from instruction_rewriter.rs (lines 14-69) for single responsibility.

/// Phase 33-16: Tail Call Classification
///
/// Classifies tail calls in JoinIR loops into three semantic categories:
///
/// 1. **LoopEntry**: First entry into the loop (main → loop_step)
///    - Occurs from the entry function's entry block
///    - Should jump directly to target (not redirect to header)
///    - Reason: The entry block IS the header block; redirecting creates self-loop
///
/// 2. **BackEdge**: Loop continuation (loop_step → loop_step)
///    - Occurs from loop body blocks (not entry function's entry block)
///    - MUST redirect to header block where PHI nodes are located
///    - Reason: PHI nodes need to merge values from all back edges
///
/// 3. **ExitJump**: Loop termination (→ k_exit)
///    - Occurs when jumping to continuation functions
///    - Handled separately via Return conversion
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TailCallKind {
    /// First entry into loop - no redirection needed
    LoopEntry,
    /// Back edge in loop - redirect to header PHI
    BackEdge,
    /// Exit from loop - becomes Return conversion
    ExitJump,
}

/// Classifies a tail call based on context
///
/// # Arguments
/// * `is_entry_func_entry_block` - True if this is the first function's first block (loop entry point)
/// * `has_loop_header_phis` - True if loop header PHI nodes exist
/// * `has_boundary` - True if JoinInlineBoundary exists (indicates loop context)
///
/// # Returns
/// The classification of this tail call
pub fn classify_tail_call(
    is_entry_func_entry_block: bool,
    has_loop_header_phis: bool,
    has_boundary: bool,
) -> TailCallKind {
    // Entry function's entry block is the loop entry point
    // It already IS at the header, so no redirection needed
    if is_entry_func_entry_block {
        return TailCallKind::LoopEntry;
    }

    // If we have boundary and header PHIs, this is a back edge
    // Must redirect to header for PHI merging
    if has_boundary && has_loop_header_phis {
        return TailCallKind::BackEdge;
    }

    // Otherwise, treat as exit (will be handled by Return conversion)
    TailCallKind::ExitJump
}

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

    #[test]
    fn test_classify_loop_entry() {
        let result = classify_tail_call(
            true, // is_entry_func_entry_block
            true, // has_loop_header_phis
            true, // has_boundary
        );
        assert_eq!(result, TailCallKind::LoopEntry);
    }

    #[test]
    fn test_classify_back_edge() {
        let result = classify_tail_call(
            false, // is_entry_func_entry_block (not entry block)
            true,  // has_loop_header_phis
            true,  // has_boundary
        );
        assert_eq!(result, TailCallKind::BackEdge);
    }

    #[test]
    fn test_classify_exit_jump() {
        let result = classify_tail_call(
            false, // is_entry_func_entry_block
            false, // has_loop_header_phis (no header PHIs)
            true,  // has_boundary
        );
        assert_eq!(result, TailCallKind::ExitJump);
    }

    #[test]
    fn test_classify_no_boundary() {
        let result = classify_tail_call(
            false, // is_entry_func_entry_block
            true,  // has_loop_header_phis
            false, // has_boundary (no boundary → exit)
        );
        assert_eq!(result, TailCallKind::ExitJump);
    }
}
