//! WebSocket event parsing for LQP client.
//!
//! Handles parsing of WebSocket messages from the League Client
//! and classifying them by event type based on URI.
//! All raw JSON data is preserved as-is — no field extraction or remapping.

use tracing::{debug, info, warn};

use super::events::{
    describe_event, EVENT_TYPE_CHAMPION_PICK, EVENT_TYPE_CHAMP_SELECT_START, EVENT_TYPE_DEATH,
    EVENT_TYPE_GAME_END, EVENT_TYPE_GAME_START, EVENT_TYPE_KILL, EVENT_TYPE_LP_CHANGE,
    EVENT_TYPE_OBJECTIVE, EVENT_TYPE_PHASE_CHANGE, EVENT_TYPE_UNKNOWN,
};

/// Parsed event with raw data preserved.
#[derive(Debug, Clone)]
pub struct ParsedEvent {
    /// Event type string derived from URI (e.g. "game_start", "lp_change").
    pub event_type: String,
    /// The raw JSON data from the API — stored as-is, no remapping.
    pub raw_data: serde_json::Value,
    /// The URI of the endpoint that triggered this event.
    pub uri: String,
}

/// Parse a WebSocket message into a parsed event with raw data.
pub fn parse_websocket_message(text: &str) -> Option<ParsedEvent> {
    // Parse the message array format: [type, callback, data]
    let value: serde_json::Value = match serde_json::from_str(text) {
        Ok(v) => v,
        Err(e) => {
            warn!("Failed to parse WebSocket message as JSON: {}", e);
            return None;
        }
    };

    // Check if it's an event message (type 8)
    if let Some(arr) = value.as_array() {
        if arr.len() >= 3 {
            let msg_type = arr.first()?.as_u64()?;

            if msg_type == 8 {
                // Event message format: [8, "OnJsonApiEvent", {"data": ..., "eventType": ..., "uri": ...}]
                let callback = arr.get(1)?.as_str()?;
                let event_data = arr.get(2)?;

                if callback == "OnJsonApiEvent" {
                    let uri = event_data.get("uri")?.as_str()?.to_string();
                    let data = event_data.get("data")?.clone();
                    let event_action = event_data
                        .get("eventType")
                        .and_then(|t| t.as_str())
                        .unwrap_or("Update");

                    let event_type = classify_event_from_uri(&uri, event_action, &data);
                    let description = describe_event(&event_type, &data);

                    info!(
                        "Event classified: type={}, uri={}, desc={}",
                        event_type, uri, description
                    );

                    return Some(ParsedEvent {
                        event_type,
                        raw_data: data,
                        uri,
                    });
                } else {
                    debug!("Unknown callback: {}", callback);
                }
            } else if msg_type == 4 {
                // Response to subscription - this is normal
                debug!("Subscription response received");
            } else if msg_type == 0 {
                // Welcome message
                info!("WebSocket welcome message received");
            } else {
                debug!("Unknown message type {msg_type} received");
            }
        }
    } else {
        debug!("Message is not an array: {:?}", value);
    }

    None
}

/// Classify an event based on the URI.
///
/// Returns an event type string. The raw data is NOT modified —
/// all original API fields are preserved as-is.
pub fn classify_event_from_uri(uri: &str, _event_action: &str, data: &serde_json::Value) -> String {
    // Handle gameflow phase changes
    if uri == "/lol-gameflow/v1/gameflow-phase" {
        return EVENT_TYPE_PHASE_CHANGE.to_string();
    }

    // Handle gameflow session updates
    if uri == "/lol-gameflow/v1/session" {
        if let Some(phase) = data.get("phase").and_then(|p| p.as_str()) {
            if phase == "InProgress" {
                return EVENT_TYPE_GAME_START.to_string();
            }
            return EVENT_TYPE_PHASE_CHANGE.to_string();
        }
        return EVENT_TYPE_UNKNOWN.to_string();
    }

    // Handle game events (kills, deaths, objectives)
    if uri == "/lol-game-events/v1/game-events" {
        return classify_game_event(data);
    }

    // Handle ready check
    if uri == "/lol-matchmaking/v1/ready-check" {
        debug!("Ready check event — not recorded");
        return EVENT_TYPE_UNKNOWN.to_string();
    }

    // Handle champion select
    if uri == "/lol-champ-select/v1/session" {
        return classify_champion_select_event(data);
    }

    // Handle end-of-game stats block
    if uri == "/lol-end-of-game/v1/eog-stats-block" {
        return EVENT_TYPE_GAME_END.to_string();
    }

    // Handle LP change notifications
    if uri == "/lol-ranked/v1/current-lp-change-notification" {
        return EVENT_TYPE_LP_CHANGE.to_string();
    }

    // Handle ranked stats updates (with UUID suffix)
    if uri.starts_with("/lol-ranked/v1/ranked-stats/") {
        return EVENT_TYPE_LP_CHANGE.to_string();
    }

    // Handle lobby
    if uri.starts_with("/lol-lobby") {
        debug!("Lobby event: {}", uri);
        return EVENT_TYPE_UNKNOWN.to_string();
    }

    debug!("Unhandled URI: {}", uri);
    EVENT_TYPE_UNKNOWN.to_string()
}

/// Classify a champion select event based on the data.
fn classify_champion_select_event(data: &serde_json::Value) -> String {
    // Check if we're in a pick phase and a champion has been selected
    if let Some(timers) = data.get("timers") {
        if let Some(phase) = timers.get("phase").and_then(|p| p.as_str()) {
            if phase == "BAN_PICK" || phase == "FINALIZATION" {
                // Check if local player has picked a champion
                if let Some(local_player_cell_id) =
                    data.get("localPlayerCellId").and_then(|id| id.as_i64())
                {
                    for team_key in &["myTeam", "theirTeam"] {
                        if let Some(team) = data.get(team_key).and_then(|t| t.as_array()) {
                            for member in team {
                                if member.get("cellId").and_then(|id| id.as_i64())
                                    == Some(local_player_cell_id)
                                {
                                    if let Some(champion_id) =
                                        member.get("championId").and_then(|id| id.as_u64())
                                    {
                                        if champion_id > 0 {
                                            return EVENT_TYPE_CHAMPION_PICK.to_string();
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    EVENT_TYPE_CHAMP_SELECT_START.to_string()
}

/// Classify an in-game event based on EventName field.
fn classify_game_event(data: &serde_json::Value) -> String {
    let event_name = data
        .get("EventName")
        .or_else(|| data.get("eventName"))
        .and_then(|n| n.as_str())
        .unwrap_or("");

    match event_name {
        "ChampionKill" => EVENT_TYPE_KILL.to_string(),
        "ChampionDeath" => EVENT_TYPE_DEATH.to_string(),
        "DragonKill" | "BaronKill" | "HeraldKill" | "RiftHeraldKill" | "ElderDragonKill" => {
            EVENT_TYPE_OBJECTIVE.to_string()
        }
        "TurretKill" | "InhibitorKill" | "NexusKill" => EVENT_TYPE_OBJECTIVE.to_string(),
        _ => {
            debug!("Unknown game event type: {}", event_name);
            EVENT_TYPE_UNKNOWN.to_string()
        }
    }
}

/// Parse events from the Live Client Data API (port 2999).
///
/// The Live Client Data API provides real-time game events including:
/// - Champion kills and deaths
/// - Objective kills (Dragon, Baron, Herald, etc.)
/// - Building destruction (Turrets, Inhibitors)
///
/// Event format from /liveclientdata/eventdata:
/// ```json
/// {
///   "EventID": 1,
///   "EventName": "ChampionKill",
///   "EventTime": 123.456,
///   "KillerName": "Player1",
///   "VictimName": "Player2",
///   "Assisters": ["Player3"],
///   ...
/// }
/// ```
pub fn parse_live_client_event(data: &serde_json::Value) -> Option<ParsedEvent> {
    let event_name = data.get("EventName").and_then(|n| n.as_str()).unwrap_or("");

    let event_type = match event_name {
        "ChampionKill" => EVENT_TYPE_KILL.to_string(),
        "ChampionDeath" => EVENT_TYPE_DEATH.to_string(),
        "DragonKill" | "BaronKill" | "HeraldKill" | "RiftHeraldKill" | "ElderDragonKill" => {
            EVENT_TYPE_OBJECTIVE.to_string()
        }
        "TurretKill" | "InhibitorKill" | "NexusKill" => EVENT_TYPE_OBJECTIVE.to_string(),
        "Multikill" | "FirstBlood" | "GameStart" | "GameEnd" => {
            // These are derived/special events — the kill/death events cover them
            return None;
        }
        _ => {
            debug!("Unknown live client event type: {}", event_name);
            return None;
        }
    };

    info!(
        "Live client event classified: type={}, name={}",
        event_type, event_name
    );

    Some(ParsedEvent {
        event_type,
        raw_data: data.clone(),
        uri: "/liveclientdata/eventdata".to_string(),
    })
}

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

    #[test]
    fn test_parse_websocket_message_invalid_json() {
        let result = parse_websocket_message("not json");
        assert!(result.is_none());
    }

    #[test]
    fn test_parse_websocket_message_empty_array() {
        let result = parse_websocket_message("[]");
        assert!(result.is_none());
    }

    #[test]
    fn test_classify_game_event_kill() {
        let data = serde_json::json!({
            "EventName": "ChampionKill",
            "KillerName": "Player1",
            "VictimName": "Player2"
        });
        assert_eq!(classify_game_event(&data), EVENT_TYPE_KILL);
    }

    #[test]
    fn test_classify_game_event_objective() {
        let data = serde_json::json!({
            "EventName": "DragonKill"
        });
        assert_eq!(classify_game_event(&data), EVENT_TYPE_OBJECTIVE);
    }

    #[test]
    fn test_classify_event_from_uri_lp_change() {
        let data = serde_json::json!({"lpChange": 22, "tier": "GOLD"});
        assert_eq!(
            classify_event_from_uri(
                "/lol-ranked/v1/current-lp-change-notification",
                "Update",
                &data
            ),
            EVENT_TYPE_LP_CHANGE
        );
    }

    #[test]
    fn test_classify_event_from_uri_game_end() {
        let data = serde_json::json!({"gameId": 123});
        assert_eq!(
            classify_event_from_uri("/lol-end-of-game/v1/eog-stats-block", "Update", &data),
            EVENT_TYPE_GAME_END
        );
    }

    #[test]
    fn test_parse_live_client_event_kill() {
        let data = serde_json::json!({
            "EventName": "ChampionKill",
            "KillerName": "Player1",
            "VictimName": "Player2",
            "Assisters": []
        });
        let result = parse_live_client_event(&data).unwrap();
        assert_eq!(result.event_type, EVENT_TYPE_KILL);
        // Raw data is preserved as-is
        assert_eq!(result.raw_data["KillerName"], "Player1");
    }
}
