//! Anthropic SSEイベントパース
//!
//! Anthropic Messages APIのSSEイベントをパースし、統一Event型に変換

use crate::llm_client::{
    ClientError,
    event::{
        BlockDelta, BlockMetadata, BlockStart, BlockStop, BlockType, DeltaContent, ErrorEvent,
        Event, PingEvent, ResponseStatus, StatusEvent, UsageEvent,
    },
};
use serde::Deserialize;

use super::AnthropicScheme;
use super::scheme_impl::{AnthropicState, PendingThinking};

/// Anthropic SSEイベントタイプ
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum AnthropicEventType {
    MessageStart,
    ContentBlockStart,
    ContentBlockDelta,
    ContentBlockStop,
    MessageDelta,
    MessageStop,
    Ping,
    Error,
}

impl AnthropicEventType {
    /// イベントタイプ文字列からパース
    pub(crate) fn parse(s: &str) -> Option<Self> {
        match s {
            "message_start" => Some(Self::MessageStart),
            "content_block_start" => Some(Self::ContentBlockStart),
            "content_block_delta" => Some(Self::ContentBlockDelta),
            "content_block_stop" => Some(Self::ContentBlockStop),
            "message_delta" => Some(Self::MessageDelta),
            "message_stop" => Some(Self::MessageStop),
            "ping" => Some(Self::Ping),
            "error" => Some(Self::Error),
            _ => None,
        }
    }
}

// ============================================================================
// SSEイベントのJSON構造
// ============================================================================

/// message_start イベント
#[derive(Debug, Deserialize)]
pub(crate) struct MessageStartEvent {
    pub message: MessageStartMessage,
}

#[allow(dead_code)]
#[derive(Debug, Deserialize)]
pub(crate) struct MessageStartMessage {
    pub id: String,
    pub model: String,
    pub usage: Option<UsageData>,
}

/// content_block_start イベント
#[derive(Debug, Deserialize)]
pub(crate) struct ContentBlockStartEvent {
    pub index: usize,
    pub content_block: ContentBlock,
}

#[allow(dead_code)]
#[derive(Debug, Deserialize)]
#[serde(tag = "type")]
pub(crate) enum ContentBlock {
    #[serde(rename = "text")]
    Text { text: String },
    #[serde(rename = "thinking")]
    Thinking {
        #[serde(default)]
        thinking: String,
        /// 非ストリーミングレスポンス由来の初期 signature（通常はストリームでは
        /// 空 → `signature_delta` で埋まる）。
        #[serde(default)]
        signature: Option<String>,
    },
    #[serde(rename = "redacted_thinking")]
    RedactedThinking {
        /// 暗号化された opaque blob。signature ではなく、まるごと
        /// `redacted_thinking.data` として送り返す必要がある。
        #[serde(default)]
        data: String,
    },
    #[serde(rename = "tool_use")]
    ToolUse {
        id: String,
        name: String,
        input: serde_json::Value,
    },
}

/// content_block_delta イベント
#[derive(Debug, Deserialize)]
pub(crate) struct ContentBlockDeltaEvent {
    pub index: usize,
    pub delta: DeltaBlock,
}

#[allow(dead_code)]
#[derive(Debug, Deserialize)]
#[serde(tag = "type")]
pub(crate) enum DeltaBlock {
    #[serde(rename = "text_delta")]
    TextDelta { text: String },
    #[serde(rename = "thinking_delta")]
    ThinkingDelta { thinking: String },
    #[serde(rename = "input_json_delta")]
    InputJsonDelta { partial_json: String },
    #[serde(rename = "signature_delta")]
    SignatureDelta { signature: String },
}

/// content_block_stop イベント
#[derive(Debug, Deserialize)]
pub(crate) struct ContentBlockStopEvent {
    pub index: usize,
}

/// message_delta イベント
#[allow(dead_code)]
#[derive(Debug, Deserialize)]
pub(crate) struct MessageDeltaEvent {
    pub delta: MessageDeltaData,
    pub usage: Option<UsageData>,
}

#[allow(dead_code)]
#[derive(Debug, Deserialize)]
pub(crate) struct MessageDeltaData {
    pub stop_reason: Option<String>,
    pub stop_sequence: Option<String>,
}

/// 使用量データ
#[derive(Debug, Deserialize)]
pub(crate) struct UsageData {
    pub input_tokens: Option<u64>,
    pub output_tokens: Option<u64>,
    pub cache_read_input_tokens: Option<u64>,
    pub cache_creation_input_tokens: Option<u64>,
}

/// エラーイベント
#[derive(Debug, Deserialize)]
pub(crate) struct ErrorEventData {
    pub error: ErrorDetail,
}

#[derive(Debug, Deserialize)]
pub(crate) struct ErrorDetail {
    #[serde(rename = "type")]
    pub error_type: String,
    pub message: String,
}

// ============================================================================
// イベント変換
// ============================================================================

impl AnthropicScheme {
    /// SSEイベントをEvent型に変換
    ///
    /// # Arguments
    /// * `event_type` - SSEイベントタイプ
    /// * `data` - イベントデータJSON文字列
    ///
    /// # Returns
    /// * `Ok(Some(Event))` - 変換成功
    /// * `Ok(None)` - イベントを無視（unknown event等）
    /// * `Err(ClientError)` - パースエラー
    pub(crate) fn parse_event(
        &self,
        event_type: &str,
        data: &str,
    ) -> Result<Option<Event>, ClientError> {
        let Some(event_type) = AnthropicEventType::parse(event_type) else {
            // Unknown event type, ignore
            return Ok(None);
        };

        match event_type {
            AnthropicEventType::MessageStart => {
                let event: MessageStartEvent = serde_json::from_str(data)?;
                // message_start時にUsageイベントがあれば出力
                if let Some(usage) = event.message.usage {
                    return Ok(Some(Event::Usage(self.convert_usage(&usage))));
                }
                // Statusイベントとして開始を通知
                Ok(Some(Event::Status(StatusEvent {
                    status: ResponseStatus::Started,
                })))
            }
            AnthropicEventType::ContentBlockStart => {
                let event: ContentBlockStartEvent = serde_json::from_str(data)?;
                Ok(Some(self.convert_block_start(&event)))
            }
            AnthropicEventType::ContentBlockDelta => {
                let event: ContentBlockDeltaEvent = serde_json::from_str(data)?;
                Ok(self.convert_block_delta(&event))
            }
            AnthropicEventType::ContentBlockStop => {
                let event: ContentBlockStopEvent = serde_json::from_str(data)?;
                // Note: BlockStopにはblock_typeが必要だが、AnthropicはStopイベントに含めない
                // Timeline層がBlockStartを追跡して正しいblock_typeを知る
                Ok(Some(Event::BlockStop(BlockStop {
                    index: event.index,
                    block_type: BlockType::Text, // Timeline層で上書きされる
                    stop_reason: None,
                    reasoning: None,
                })))
            }
            AnthropicEventType::MessageDelta => {
                let event: MessageDeltaEvent = serde_json::from_str(data)?;
                // Usage情報があれば出力
                if let Some(usage) = event.usage {
                    return Ok(Some(Event::Usage(self.convert_usage(&usage))));
                }
                Ok(None)
            }
            AnthropicEventType::MessageStop => Ok(Some(Event::Status(StatusEvent {
                status: ResponseStatus::Completed,
            }))),
            AnthropicEventType::Ping => Ok(Some(Event::Ping(PingEvent { timestamp: None }))),
            AnthropicEventType::Error => {
                let event: ErrorEventData = serde_json::from_str(data)?;
                Ok(Some(Event::Error(ErrorEvent {
                    code: Some(event.error.error_type),
                    message: event.error.message,
                })))
            }
        }
    }

    fn convert_block_start(&self, event: &ContentBlockStartEvent) -> Event {
        let (block_type, metadata) = match &event.content_block {
            ContentBlock::Text { .. } => (BlockType::Text, BlockMetadata::Text),
            ContentBlock::Thinking { .. } | ContentBlock::RedactedThinking { .. } => {
                (BlockType::Thinking, BlockMetadata::Thinking)
            }
            ContentBlock::ToolUse { id, name, .. } => (
                BlockType::ToolUse,
                BlockMetadata::ToolUse {
                    id: id.clone(),
                    name: name.clone(),
                },
            ),
        };

        Event::BlockStart(BlockStart {
            index: event.index,
            block_type,
            metadata,
        })
    }

    fn convert_block_delta(&self, event: &ContentBlockDeltaEvent) -> Option<Event> {
        let delta = match &event.delta {
            DeltaBlock::TextDelta { text } => DeltaContent::Text(text.clone()),
            DeltaBlock::ThinkingDelta { thinking } => DeltaContent::Thinking(thinking.clone()),
            DeltaBlock::InputJsonDelta { partial_json } => {
                DeltaContent::InputJson(partial_json.clone())
            }
            DeltaBlock::SignatureDelta { .. } => {
                // signature_delta は無視
                return None;
            }
        };

        Some(Event::BlockDelta(BlockDelta {
            index: event.index,
            delta,
        }))
    }

    /// state を持ち回す上位パース。
    ///
    /// `parse_event` の単発 Event に加えて、以下を行う:
    /// - `content_block_stop` の `block_type` を直前の Start 値で書き戻す
    /// - `thinking` / `redacted_thinking` ブロックの本体・signature・data を
    ///   `state.pending_thinking` に蓄積し、`content_block_stop` の Thinking
    ///   BlockStop metadata に載せる
    /// - `signature_delta` を蓄積（Stream channel には流さず、reasoning metadata
    ///   にだけ反映する）
    pub(crate) fn parse_with_state(
        &self,
        event_type: &str,
        data: &str,
        state: &mut AnthropicState,
    ) -> Result<Vec<Event>, ClientError> {
        let Some(parsed_event_type) = AnthropicEventType::parse(event_type) else {
            return Ok(Vec::new());
        };

        // signature_delta はストリーム表示には流さず、state にだけ蓄積。
        // それ以外は parse_event で標準 Event 化する。
        let mut emitted: Vec<Event> = Vec::new();

        match parsed_event_type {
            AnthropicEventType::ContentBlockStart => {
                let raw: ContentBlockStartEvent = serde_json::from_str(data)?;
                state.current_block_type = Some(match &raw.content_block {
                    ContentBlock::Text { .. } => BlockType::Text,
                    ContentBlock::Thinking { .. } | ContentBlock::RedactedThinking { .. } => {
                        BlockType::Thinking
                    }
                    ContentBlock::ToolUse { .. } => BlockType::ToolUse,
                });
                match &raw.content_block {
                    ContentBlock::Thinking {
                        thinking,
                        signature,
                    } => {
                        state.pending_thinking = Some(PendingThinking {
                            text: thinking.clone(),
                            signature: signature.clone(),
                            redacted_data: None,
                        });
                    }
                    ContentBlock::RedactedThinking { data: blob } => {
                        state.pending_thinking = Some(PendingThinking {
                            text: String::new(),
                            signature: None,
                            redacted_data: Some(blob.clone()),
                        });
                    }
                    _ => {}
                }
                emitted.push(self.convert_block_start(&raw));
            }
            AnthropicEventType::ContentBlockDelta => {
                let raw: ContentBlockDeltaEvent = serde_json::from_str(data)?;
                match &raw.delta {
                    DeltaBlock::ThinkingDelta { thinking } => {
                        if let Some(pending) = state.pending_thinking.as_mut() {
                            pending.text.push_str(thinking);
                        }
                        emitted.push(Event::BlockDelta(BlockDelta {
                            index: raw.index,
                            delta: DeltaContent::Thinking(thinking.clone()),
                        }));
                    }
                    DeltaBlock::SignatureDelta { signature } => {
                        if let Some(pending) = state.pending_thinking.as_mut() {
                            // 通常 1 回しか来ないが、複数 fragment 来ても連結しておく
                            match &mut pending.signature {
                                Some(acc) => acc.push_str(signature),
                                None => pending.signature = Some(signature.clone()),
                            }
                        }
                    }
                    DeltaBlock::TextDelta { text } => {
                        emitted.push(Event::BlockDelta(BlockDelta {
                            index: raw.index,
                            delta: DeltaContent::Text(text.clone()),
                        }));
                    }
                    DeltaBlock::InputJsonDelta { partial_json } => {
                        emitted.push(Event::BlockDelta(BlockDelta {
                            index: raw.index,
                            delta: DeltaContent::InputJson(partial_json.clone()),
                        }));
                    }
                }
            }
            AnthropicEventType::ContentBlockStop => {
                let raw: ContentBlockStopEvent = serde_json::from_str(data)?;
                let block_type = state.current_block_type.take().unwrap_or(BlockType::Text);
                let reasoning = if matches!(block_type, BlockType::Thinking) {
                    state
                        .pending_thinking
                        .take()
                        .map(PendingThinking::into_reasoning)
                } else {
                    state.pending_thinking.take();
                    None
                };
                emitted.push(Event::BlockStop(BlockStop {
                    index: raw.index,
                    block_type,
                    stop_reason: None,
                    reasoning,
                }));
            }
            // 残りは state を必要としない。既存 parse_event に委譲。
            _ => {
                if let Some(event) = self.parse_event(event_type, data)? {
                    emitted.push(event);
                }
            }
        }

        Ok(emitted)
    }

    fn convert_usage(&self, usage: &UsageData) -> UsageEvent {
        // Anthropic の `input_tokens` は **キャッシュ外** の入力トークンのみで、
        // プロンプト全長は input_tokens + cache_read + cache_creation。
        // UsageEvent の `input_tokens` には「占有量（プロンプト全長）」を載せる
        // 規約に合わせて、ここでキャッシュ分を足し込む。
        // cache_read_input_tokens / cache_creation_input_tokens は内訳として
        // 別フィールドに残るので、料金計算側で `input - cache_read - cache_creation`
        // により非キャッシュ入力分は逆算可能。
        let raw_input = usage.input_tokens.unwrap_or(0);
        let cache_read = usage.cache_read_input_tokens.unwrap_or(0);
        let cache_creation = usage.cache_creation_input_tokens.unwrap_or(0);
        let input_total = raw_input + cache_read + cache_creation;
        let output = usage.output_tokens.unwrap_or(0);

        UsageEvent {
            input_tokens: usage.input_tokens.map(|_| input_total),
            output_tokens: usage.output_tokens,
            total_tokens: Some(input_total + output),
            cache_read_input_tokens: usage.cache_read_input_tokens,
            cache_creation_input_tokens: usage.cache_creation_input_tokens,
        }
    }
}

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

    #[test]
    fn test_parse_message_start() {
        let scheme = AnthropicScheme::new();
        let data = r#"{"type":"message_start","message":{"id":"msg_123","type":"message","role":"assistant","content":[],"model":"claude-sonnet-4-20250514","stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":10,"output_tokens":0}}}"#;

        let event = scheme.parse_event("message_start", data).unwrap().unwrap();
        match event {
            Event::Usage(u) => {
                // キャッシュなしなので input_total = raw_input = 10
                assert_eq!(u.input_tokens, Some(10));
            }
            _ => panic!("Expected Usage event"),
        }
    }

    #[test]
    fn test_convert_usage_includes_cache_in_input_total() {
        // Anthropic の input_tokens はキャッシュ外のみで、占有量は
        // input + cache_read + cache_creation。
        // UsageEvent.input_tokens は占有量に正規化される。
        let scheme = AnthropicScheme::new();
        let usage = UsageData {
            input_tokens: Some(100),
            output_tokens: Some(50),
            cache_read_input_tokens: Some(800),
            cache_creation_input_tokens: Some(200),
        };
        let event = scheme.convert_usage(&usage);
        // 100 + 800 + 200 = 1100
        assert_eq!(event.input_tokens, Some(1100));
        assert_eq!(event.cache_read_input_tokens, Some(800));
        assert_eq!(event.cache_creation_input_tokens, Some(200));
        assert_eq!(event.total_tokens, Some(1150));
    }

    #[test]
    fn test_parse_content_block_start_text() {
        let scheme = AnthropicScheme::new();
        let data =
            r#"{"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}"#;

        let event = scheme
            .parse_event("content_block_start", data)
            .unwrap()
            .unwrap();
        match event {
            Event::BlockStart(s) => {
                assert_eq!(s.index, 0);
                assert_eq!(s.block_type, BlockType::Text);
            }
            _ => panic!("Expected BlockStart event"),
        }
    }

    #[test]
    fn test_parse_content_block_delta_text() {
        let scheme = AnthropicScheme::new();
        let data = r#"{"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello"}}"#;

        let event = scheme
            .parse_event("content_block_delta", data)
            .unwrap()
            .unwrap();
        match event {
            Event::BlockDelta(d) => {
                assert_eq!(d.index, 0);
                match d.delta {
                    DeltaContent::Text(t) => assert_eq!(t, "Hello"),
                    _ => panic!("Expected Text delta"),
                }
            }
            _ => panic!("Expected BlockDelta event"),
        }
    }

    #[test]
    fn test_parse_tool_use_start() {
        let scheme = AnthropicScheme::new();
        let data = r#"{"type":"content_block_start","index":1,"content_block":{"type":"tool_use","id":"toolu_123","name":"get_weather","input":{}}}"#;

        let event = scheme
            .parse_event("content_block_start", data)
            .unwrap()
            .unwrap();
        match event {
            Event::BlockStart(s) => {
                assert_eq!(s.block_type, BlockType::ToolUse);
                match s.metadata {
                    BlockMetadata::ToolUse { id, name } => {
                        assert_eq!(id, "toolu_123");
                        assert_eq!(name, "get_weather");
                    }
                    _ => panic!("Expected ToolUse metadata"),
                }
            }
            _ => panic!("Expected BlockStart event"),
        }
    }

    #[test]
    fn thinking_block_stop_carries_reasoning_with_signature() {
        // thinking ブロックが完了したら reasoning metadata に text+signature が乗ること
        let scheme = AnthropicScheme::new();
        let mut state = AnthropicState::default();

        let evs = scheme
            .parse_with_state(
                "content_block_start",
                r#"{"type":"content_block_start","index":0,"content_block":{"type":"thinking","thinking":""}}"#,
                &mut state,
            )
            .unwrap();
        assert!(matches!(evs[0], Event::BlockStart(_)));

        scheme
            .parse_with_state(
                "content_block_delta",
                r#"{"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":"hello "}}"#,
                &mut state,
            )
            .unwrap();
        scheme
            .parse_with_state(
                "content_block_delta",
                r#"{"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":"world"}}"#,
                &mut state,
            )
            .unwrap();
        scheme
            .parse_with_state(
                "content_block_delta",
                r#"{"type":"content_block_delta","index":0,"delta":{"type":"signature_delta","signature":"SIG-XYZ"}}"#,
                &mut state,
            )
            .unwrap();

        let stop_evs = scheme
            .parse_with_state(
                "content_block_stop",
                r#"{"type":"content_block_stop","index":0}"#,
                &mut state,
            )
            .unwrap();
        assert_eq!(stop_evs.len(), 1);
        let Event::BlockStop(stop) = &stop_evs[0] else {
            panic!("expected BlockStop, got {:?}", stop_evs[0]);
        };
        let reasoning = stop.reasoning.as_ref().expect("reasoning metadata");
        assert_eq!(reasoning.text.as_deref(), Some("hello world"));
        assert_eq!(reasoning.signature.as_deref(), Some("SIG-XYZ"));
        assert!(reasoning.encrypted_content.is_none());
    }

    #[test]
    fn redacted_thinking_stop_carries_reasoning_with_data() {
        let scheme = AnthropicScheme::new();
        let mut state = AnthropicState::default();

        scheme
            .parse_with_state(
                "content_block_start",
                r#"{"type":"content_block_start","index":0,"content_block":{"type":"redacted_thinking","data":"opaque-blob"}}"#,
                &mut state,
            )
            .unwrap();
        let stop_evs = scheme
            .parse_with_state(
                "content_block_stop",
                r#"{"type":"content_block_stop","index":0}"#,
                &mut state,
            )
            .unwrap();
        assert_eq!(stop_evs.len(), 1);
        let Event::BlockStop(stop) = &stop_evs[0] else {
            panic!("expected BlockStop");
        };
        let reasoning = stop.reasoning.as_ref().expect("reasoning metadata");
        assert_eq!(reasoning.text.as_deref(), Some(""));
        assert!(reasoning.signature.is_none());
        assert_eq!(reasoning.encrypted_content.as_deref(), Some("opaque-blob"));
    }

    #[test]
    fn text_block_stop_has_no_reasoning_metadata() {
        let scheme = AnthropicScheme::new();
        let mut state = AnthropicState::default();

        scheme
            .parse_with_state(
                "content_block_start",
                r#"{"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}"#,
                &mut state,
            )
            .unwrap();
        scheme
            .parse_with_state(
                "content_block_delta",
                r#"{"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"hi"}}"#,
                &mut state,
            )
            .unwrap();
        let stop_evs = scheme
            .parse_with_state(
                "content_block_stop",
                r#"{"type":"content_block_stop","index":0}"#,
                &mut state,
            )
            .unwrap();
        assert_eq!(stop_evs.len(), 1);
        let Event::BlockStop(stop) = &stop_evs[0] else {
            panic!("expected BlockStop");
        };
        assert!(stop.reasoning.is_none());
    }

    #[test]
    fn test_parse_ping() {
        let scheme = AnthropicScheme::new();
        let data = r#"{"type":"ping"}"#;

        let event = scheme.parse_event("ping", data).unwrap().unwrap();
        match event {
            Event::Ping(_) => {}
            _ => panic!("Expected Ping event"),
        }
    }
}
