//! Timeline module for storing game events and mapping them to video timestamps.

mod mapper;
mod store;

pub use mapper::EventMapper;
pub use store::{MetadataUpdate, RecordingMetadata, TimelineStore, TimestampedEvent};

use chrono::{DateTime, Duration, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;

/// A timeline of events for a recording.
/// Stores raw API responses for maximum flexibility and future-proofing.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Timeline {
    /// Recording ID.
    pub recording_id: Uuid,
    /// Recording start time.
    pub start_time: DateTime<Utc>,
    /// Recording end time.
    pub end_time: Option<DateTime<Utc>>,
    /// Total duration in seconds.
    pub duration_secs: i64,
    /// Events in the timeline.
    pub events: Vec<TimestampedEvent>,
    /// Game ID if available.
    #[serde(default)]
    pub game_id: Option<u64>,
    /// Raw session data from `/lol-gameflow/v1/session`.
    #[serde(default)]
    pub raw_session: Option<serde_json::Value>,
    /// Raw summoner data from `/lol-summoner/v1/current-summoner`.
    #[serde(default)]
    pub raw_summoner: Option<serde_json::Value>,
    /// Raw champion select data from `/lol-champ-select/v1/session`.
    #[serde(default)]
    pub raw_champion_select: Option<serde_json::Value>,
    /// Raw rune page data from `/lol-perks/v1/currentpage`.
    #[serde(default)]
    pub raw_rune_page: Option<serde_json::Value>,
    /// Raw live client data from `/liveclientdata/allgamedata`.
    #[serde(default)]
    pub raw_live_client_data: Option<serde_json::Value>,
    /// Raw end-of-game stats from `/lol-end-of-game/v1/eog-stats-block`.
    #[serde(default)]
    pub raw_end_game_stats: Option<serde_json::Value>,
    /// Video file name (just the filename, not the full path).
    #[serde(default)]
    pub video_file: Option<String>,
}

impl Timeline {
    /// Get the duration as chrono Duration.
    pub fn duration(&self) -> Duration {
        Duration::seconds(self.duration_secs)
    }
}

impl Timeline {
    /// Create a new timeline for a recording.
    pub fn new(recording_id: Uuid) -> Self {
        Self {
            recording_id,
            start_time: Utc::now(),
            end_time: None,
            duration_secs: 0,
            events: Vec::new(),
            game_id: None,
            raw_session: None,
            raw_summoner: None,
            raw_champion_select: None,
            raw_rune_page: None,
            raw_live_client_data: None,
            raw_end_game_stats: None,
            video_file: None,
        }
    }

    /// Add an event to the timeline with raw data.
    pub fn add_event(
        &mut self,
        event_type: &str,
        description: &str,
        video_timestamp: Duration,
        game_timestamp: Option<Duration>,
        raw_data: serde_json::Value,
        uri: String,
    ) {
        let timestamped = TimestampedEvent {
            video_timestamp,
            game_timestamp,
            timestamp: Utc::now(),
            event_type: event_type.to_string(),
            description: description.to_string(),
            raw_data,
            uri,
        };

        self.events.push(timestamped);
    }

    /// Finalize the timeline.
    pub fn finalize(&mut self) {
        self.end_time = Some(Utc::now());
        self.duration_secs = (self.end_time.unwrap() - self.start_time).num_seconds();
    }

    /// Get events within a time range.
    pub fn events_in_range(&self, start: Duration, end: Duration) -> Vec<&TimestampedEvent> {
        self.events
            .iter()
            .filter(|e| e.video_timestamp >= start && e.video_timestamp <= end)
            .collect()
    }

    /// Get events of a specific type.
    pub fn events_of_type(&self, event_type: &str) -> Vec<&TimestampedEvent> {
        self.events
            .iter()
            .filter(|e| e.event_type == event_type)
            .collect()
    }

    /// Get the number of events.
    pub fn event_count(&self) -> usize {
        self.events.len()
    }

    /// Export timeline to JSON.
    pub fn to_json(&self) -> Result<String, serde_json::Error> {
        serde_json::to_string_pretty(self)
    }

    /// Export timeline to CSV.
    pub fn to_csv(&self) -> String {
        let mut csv =
            String::from("video_timestamp,game_timestamp,event_type,description,timestamp\n");

        for event in &self.events {
            let video_ts = format_timestamp(event.video_timestamp);
            let game_ts = event
                .game_timestamp
                .map(format_timestamp)
                .unwrap_or_default();

            csv.push_str(&format!(
                "{},{},{},{},{}\n",
                video_ts,
                game_ts,
                event.event_type,
                event.description,
                event.timestamp.to_rfc3339(),
            ));
        }

        csv
    }
}

/// Format a duration as HH:MM:SS.mmm
fn format_timestamp(duration: Duration) -> String {
    let total_ms = duration.num_milliseconds();
    let hours = total_ms / 3_600_000;
    let minutes = (total_ms % 3_600_000) / 60_000;
    let seconds = (total_ms % 60_000) / 1000;
    let millis = total_ms % 1000;

    format!("{:02}:{:02}:{:02}.{:03}", hours, minutes, seconds, millis)
}

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

    #[test]
    fn test_timeline_creation() {
        let id = Uuid::new_v4();
        let timeline = Timeline::new(id);

        assert_eq!(timeline.recording_id, id);
        assert!(timeline.events.is_empty());
    }

    #[test]
    fn test_add_event() {
        let id = Uuid::new_v4();
        let mut timeline = Timeline::new(id);

        let raw_data = serde_json::json!({
            "killer": "Player1",
            "victim": "Player2"
        });

        timeline.add_event(
            "kill",
            "Player1 killed Player2",
            Duration::seconds(5),
            Some(Duration::seconds(120)),
            raw_data,
            "/lol-game-events/v1/events".to_string(),
        );

        assert_eq!(timeline.event_count(), 1);
        assert_eq!(timeline.events[0].event_type, "kill");
    }

    #[test]
    fn test_format_timestamp() {
        let duration = Duration::milliseconds(3723456); // 1:02:03.456
        let formatted = format_timestamp(duration);
        assert_eq!(formatted, "01:02:03.456");
    }
}
