/*!
 * Phase 136 Step 6/7: MetadataContext - Metadata/Span/Hint 管理の統一箱
 *
 * 責務:
 * - current_span: 現在の AST span（命令アノテーション用）
 * - source_file: ソースファイルヒント（メタデータ用）
 * - hint_sink: 型推論ヒント（ゼロコストガイダンス）
 * - current_region_stack: Region 観測用スタック（NYASH_REGION_TRACE=1 デバッグ用）
 *
 * 設計:
 * - HintSink は no-op デフォルトだが、将来の型推論最適化に備える
 * - Span は命令単位で保持され、エラー報告・デバッグ情報生成に使用
 * - source_file は関数メタデータに伝播
 * - current_region_stack は開発用トレース（本番コストゼロ）
 */

use crate::ast::Span;
use crate::mir::hints::HintSink;
use crate::mir::region::RegionId;

/// Phase 136 Step 6/7: Metadata/Span/Hint 管理を統一した構造体だよ
#[derive(Debug, Clone)]
pub struct MetadataContext {
    /// 現在の AST span（命令アノテーション用）
    pub(super) current_span: Span,

    /// ソースファイルヒント（関数メタデータに伝播）
    pub(super) source_file: Option<String>,

    /// 型推論ヒント（ゼロコストガイダンス）
    pub(super) hint_sink: HintSink,

    /// Region 観測用のスタック（NYASH_REGION_TRACE=1 のデバッグ専用）
    /// - FunctionRegion がルート
    /// - 開発時のみ使用（本番コストゼロ）
    pub(super) current_region_stack: Vec<RegionId>,
}

impl MetadataContext {
    /// 新規 MetadataContext を生成（デフォルト状態）
    pub(crate) fn new() -> Self {
        Self {
            current_span: Span::unknown(),
            source_file: None,
            hint_sink: HintSink::new(),
            current_region_stack: Vec::new(),
        }
    }

    // ---- Span 管理 ----

    /// 現在の span を取得
    #[inline]
    pub(crate) fn current_span(&self) -> Span {
        self.current_span
    }

    /// 現在の span を設定
    #[inline]
    pub(crate) fn set_current_span(&mut self, span: Span) {
        self.current_span = span;
    }

    // ---- Source File 管理 ----

    /// ソースファイルヒントを設定
    #[inline]
    pub(crate) fn set_source_file<S: Into<String>>(&mut self, source: S) {
        self.source_file = Some(source.into());
    }

    /// ソースファイルヒントをクリア
    #[inline]
    pub(crate) fn clear_source_file(&mut self) {
        self.source_file = None;
    }

    /// 現在のソースファイルヒントを取得
    #[inline]
    pub(crate) fn current_source_file(&self) -> Option<String> {
        self.source_file.clone()
    }

    // ---- Hint Sink 管理（型推論ガイダンス）----

    /// スコープ開始ヒント（no-op デフォルト）
    #[inline]
    pub(crate) fn hint_scope_enter(&mut self, id: u32) {
        self.hint_sink.scope_enter(id);
    }

    /// スコープ終了ヒント（no-op デフォルト）
    #[inline]
    pub(crate) fn hint_scope_leave(&mut self, id: u32) {
        self.hint_sink.scope_leave(id);
    }

    /// Join 結果ヒント（no-op デフォルト）
    #[inline]
    pub(crate) fn hint_join_result<S: Into<String>>(&mut self, var: S) {
        self.hint_sink.join_result(var.into());
    }

    // ---- Region Stack 管理（デバッグ専用）----

    /// Region スタックに push（NYASH_REGION_TRACE=1 専用）
    #[inline]
    pub(crate) fn push_region(&mut self, region_id: RegionId) {
        self.current_region_stack.push(region_id);
    }

    /// Region スタックから pop（NYASH_REGION_TRACE=1 専用）
    #[inline]
    pub(crate) fn pop_region(&mut self) -> Option<RegionId> {
        self.current_region_stack.pop()
    }

    /// 現在の Region スタックを取得（読み取り専用）
    #[inline]
    pub(crate) fn current_region_stack(&self) -> &[RegionId] {
        &self.current_region_stack
    }
}

impl Default for MetadataContext {
    fn default() -> Self {
        Self::new()
    }
}

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

    #[test]
    fn test_metadata_context_creation() {
        let ctx = MetadataContext::new();
        assert!(ctx.source_file.is_none());
        assert_eq!(ctx.current_region_stack.len(), 0);
    }

    #[test]
    fn test_span_management() {
        let mut ctx = MetadataContext::new();
        let span = Span::new(0, 10, 1, 1);
        ctx.set_current_span(span);
        assert_eq!(ctx.current_span().start, 0);
        assert_eq!(ctx.current_span().end, 10);
    }

    #[test]
    fn test_source_file_management() {
        let mut ctx = MetadataContext::new();
        ctx.set_source_file("test.hako");
        assert_eq!(ctx.current_source_file(), Some("test.hako".to_string()));
        ctx.clear_source_file();
        assert!(ctx.current_source_file().is_none());
    }

    #[test]
    fn test_region_stack() {
        let mut ctx = MetadataContext::new();
        let region1 = RegionId(1);
        let region2 = RegionId(2);

        ctx.push_region(region1);
        ctx.push_region(region2);
        assert_eq!(ctx.current_region_stack().len(), 2);

        assert_eq!(ctx.pop_region(), Some(region2));
        assert_eq!(ctx.pop_region(), Some(region1));
        assert_eq!(ctx.pop_region(), None);
    }

    #[test]
    fn test_hint_operations_no_panic() {
        let mut ctx = MetadataContext::new();
        // These should not panic (no-op by default)
        ctx.hint_scope_enter(1);
        ctx.hint_scope_leave(1);
        ctx.hint_join_result("test_var");
    }
}
