use crate::session::{TurnBlock, TurnSnapshot};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ReasoningVisibility {
    Hidden,
    Collapsed,
    Expanded,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ToolVisibility {
    Hidden,
    Compact,
    Detailed,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PresentationPolicy {
    pub live: bool,
    pub reasoning: ReasoningVisibility,
    pub tools: ToolVisibility,
}

impl PresentationPolicy {
    pub const fn interactive() -> Self {
        Self {
            live: true,
            reasoning: ReasoningVisibility::Collapsed,
            tools: ToolVisibility::Detailed,
        }
    }

    pub const fn external(live: bool) -> Self {
        Self {
            live,
            reasoning: ReasoningVisibility::Hidden,
            tools: ToolVisibility::Compact,
        }
    }

    pub const fn unattended() -> Self {
        Self {
            live: false,
            reasoning: ReasoningVisibility::Hidden,
            tools: ToolVisibility::Hidden,
        }
    }
}

/// Produce the immutable view that is allowed to leave the Gateway core.
///
/// Presentation filtering deliberately clones the snapshot. Conversation
/// history and the authoritative TurnController state remain untouched.
pub fn project_snapshot(snapshot: &TurnSnapshot, policy: PresentationPolicy) -> TurnSnapshot {
    let mut projected = snapshot.clone();
    projected.blocks.retain_mut(|block| match block {
        TurnBlock::Reasoning { .. } => policy.reasoning != ReasoningVisibility::Hidden,
        TurnBlock::Assistant { .. } => true,
        TurnBlock::Tool {
            arguments, preview, ..
        } => match policy.tools {
            ToolVisibility::Hidden => false,
            ToolVisibility::Compact => {
                *arguments = serde_json::Value::Null;
                *preview = None;
                true
            }
            ToolVisibility::Detailed => true,
        },
    });
    projected
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::session::{BlockId, ToolStatus, TurnId, TurnPhase, TurnState, TurnStatus};

    fn snapshot() -> TurnSnapshot {
        TurnState {
            id: TurnId("turn".into()),
            session_id: "session".into(),
            message_id: "message".into(),
            revision: 3,
            status: TurnStatus::Running,
            phase: TurnPhase::Acting,
            blocks: vec![
                TurnBlock::Reasoning {
                    id: BlockId("reasoning".into()),
                    iteration: 0,
                    text: "private chain".into(),
                },
                TurnBlock::Assistant {
                    id: BlockId("text".into()),
                    iteration: 0,
                    text: "visible".into(),
                },
                TurnBlock::Tool {
                    id: "tool".into(),
                    iteration: 0,
                    name: "bash".into(),
                    arguments: serde_json::json!({"token": "secret"}),
                    status: ToolStatus::Completed,
                    preview: Some("sensitive output".into()),
                },
            ],
            usage: None,
            error: None,
        }
    }

    #[test]
    fn external_projection_removes_reasoning_and_tool_details_without_mutating_source() {
        let source = snapshot();
        let projected = project_snapshot(&source, PresentationPolicy::external(true));

        assert_eq!(projected.blocks.len(), 2);
        assert!(matches!(projected.blocks[0], TurnBlock::Assistant { .. }));
        assert!(matches!(
            &projected.blocks[1],
            TurnBlock::Tool {
                arguments: serde_json::Value::Null,
                preview: None,
                ..
            }
        ));
        assert_eq!(source.blocks.len(), 3);
        assert!(matches!(source.blocks[0], TurnBlock::Reasoning { .. }));
    }

    #[test]
    fn unattended_projection_keeps_only_assistant_blocks() {
        let projected = project_snapshot(&snapshot(), PresentationPolicy::unattended());

        assert_eq!(projected.blocks.len(), 1);
        assert!(matches!(projected.blocks[0], TurnBlock::Assistant { .. }));
    }
}
