//! OTLP (OpenTelemetry Protocol) JSON type definitions
//!
//! These types represent the OTLP log export request structure as defined by
//! the OpenTelemetry specification. They are used for deserializing incoming
//! OTLP log data from various AI coding assistants.

use serde::Deserialize;

/// Top-level OTLP logs export request
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ExportLogsServiceRequest {
    pub resource_logs: Vec<ResourceLogs>,
}

/// Resource-level logs containing scope logs
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ResourceLogs {
    pub resource: Option<Resource>,
    pub scope_logs: Vec<ScopeLogs>,
}

/// Resource metadata (e.g., process.pid)
#[derive(Debug, Deserialize)]
pub struct Resource {
    pub attributes: Option<Vec<KeyValue>>,
}

/// Scope-level logs containing individual log records
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ScopeLogs {
    pub log_records: Vec<LogRecord>,
}

/// Individual log record with timestamp and attributes
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LogRecord {
    pub time_unix_nano: Option<String>,
    pub attributes: Option<Vec<KeyValue>>,
}

/// Key-value pair for OTLP attributes
#[derive(Debug, Deserialize)]
pub struct KeyValue {
    pub key: String,
    pub value: AnyValueTyped,
}

/// Typed value container supporting string, int, and double values
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AnyValueTyped {
    pub string_value: Option<String>,
    /// Integer value - Codex sends some fields (cached_token_count, reasoning_token_count) as int
    pub int_value: Option<i64>,
    pub double_value: Option<f64>,
}

impl AnyValueTyped {
    /// Get an i64 value from either string_value or int_value
    /// Codex sends some fields as int_value while Claude sends as string_value
    pub fn as_i64(&self) -> Option<i64> {
        // Try int_value first (Codex sometimes uses this)
        if let Some(val) = self.int_value {
            return Some(val);
        }
        // Fall back to parsing string_value
        self.string_value.as_ref().and_then(|s| s.parse().ok())
    }

    /// Get an f64 value from string_value or double_value
    pub fn as_f64(&self) -> Option<f64> {
        if let Some(val) = self.double_value {
            return Some(val);
        }
        self.string_value.as_ref().and_then(|s| s.parse().ok())
    }
}
