use std::borrow::Cow;

use rsigma_parser::fieldpath::{first_unescaped, unescape_brackets};
use serde_json::Value;

use super::{Event, EventValue};

/// Maximum nesting depth for recursive JSON traversal.
const MAX_NESTING_DEPTH: usize = 64;

/// Zero-copy event backed by `serde_json::Value`.
///
/// Supports both borrowed (`&Value`) and owned (`Value`) backing via `Cow`.
/// This is the primary implementation for JSON/NDJSON input.
///
/// Flat keys are checked first: `"actor.id"` as a single key takes precedence
/// over `{"actor": {"id": ...}}` nested traversal.
#[derive(Debug)]
pub struct JsonEvent<'a> {
    inner: Cow<'a, Value>,
}

impl<'a> JsonEvent<'a> {
    /// Wrap a borrowed JSON value as an event.
    pub fn borrow(v: &'a Value) -> Self {
        Self {
            inner: Cow::Borrowed(v),
        }
    }

    /// Wrap an owned JSON value as an event.
    pub fn owned(v: Value) -> Self {
        Self {
            inner: Cow::Owned(v),
        }
    }
}

impl<'a> From<&'a Value> for JsonEvent<'a> {
    fn from(v: &'a Value) -> Self {
        Self::borrow(v)
    }
}

impl From<Value> for JsonEvent<'static> {
    fn from(v: Value) -> Self {
        Self::owned(v)
    }
}

impl<'a> Event for JsonEvent<'a> {
    /// Get a field value by name, supporting dot-notation for nested access.
    ///
    /// Checks for a flat key first (exact match), then falls back to
    /// dot-separated traversal. When a path segment crosses an array, every
    /// element is followed and all terminal values are collected: a single
    /// hit is returned as-is, multiple hits are returned as an
    /// [`EventValue::Array`] so the matcher applies any-member semantics
    /// (rather than only testing the first element).
    fn get_field(&self, path: &str) -> Option<EventValue<'_>> {
        let value: &Value = &self.inner;

        if let Some(obj) = value.as_object()
            && let Some(v) = obj.get(path)
        {
            return Some(EventValue::from(v));
        }

        if path.contains('.') || path.contains('[') || path.contains('\\') {
            let ops = parse_path_ops(path);
            let mut collected: Vec<EventValue<'_>> = Vec::new();
            collect_by_ops(value, &ops, &mut collected);
            return match collected.len() {
                0 => None,
                1 => collected.pop(),
                _ => Some(EventValue::Array(collected)),
            };
        }

        None
    }

    /// Check if any string value in the event satisfies a predicate.
    ///
    /// Short-circuits on the first match, avoiding the allocation of
    /// collecting all string values into a `Vec`.
    fn any_string_value(&self, pred: &dyn Fn(&str) -> bool) -> bool {
        any_string_value_json(&self.inner, pred, MAX_NESTING_DEPTH)
    }

    /// Iterate over all string values in the event (for keyword detection).
    ///
    /// Recursively walks the entire event object and yields every string
    /// value found, including inside nested objects and arrays. Traversal
    /// is capped at 64 levels of nesting to prevent stack overflow.
    fn all_string_values(&self) -> Vec<Cow<'_, str>> {
        let mut values = Vec::new();
        collect_string_values_json(&self.inner, &mut values, MAX_NESTING_DEPTH);
        values
    }

    fn to_json(&self) -> Value {
        self.inner.as_ref().clone()
    }

    /// Walk every leaf field in the event and yield dot-joined paths.
    /// Intermediate object names (`actor` for `{"actor":{"id":"x"}}`)
    /// are NOT emitted; only the leaves (`actor.id`) appear. This
    /// matches typical Sigma rules, which reference nested values via
    /// dot-notation; emitting the intermediate name would falsely flag
    /// every parent object as "unknown" in the gap signal even when
    /// the rule references a child path. Top-level scalar fields
    /// (`{"actor":"alice"}`) emit `actor` because they ARE leaves.
    /// Arrays contribute their parent path once; per-index suffixes
    /// are not emitted.
    fn field_keys(&self) -> Vec<Cow<'_, str>> {
        let mut out = Vec::new();
        collect_field_keys(&self.inner, "", &mut out, MAX_NESTING_DEPTH);
        out
    }
}

/// A single field-path navigation step.
enum PathOp<'a> {
    /// Object key lookup (bracket-unescaped). Distributes over arrays (implicit
    /// any-member).
    Key(Cow<'a, str>),
    /// Positional array index, possibly negative. Selects one element; never
    /// fans out.
    Index(i64),
}

/// Resolve a positional index against an array length. Negative indices count
/// from the end (`-1` is the last element); out-of-range yields `None`.
pub(crate) fn resolve_array_index(index: i64, len: usize) -> Option<usize> {
    if index >= 0 {
        usize::try_from(index).ok().filter(|&i| i < len)
    } else {
        usize::try_from(index.unsigned_abs())
            .ok()
            .and_then(|abs| len.checked_sub(abs))
    }
}

/// Parse a dot path into navigation ops, recognizing positional `name[N]`
/// (and chained `name[N][M]`, with negative indices counting from the end). A
/// bracket group that is not an integer degrades to a literal object key so it
/// simply fails to match.
fn parse_path_ops(path: &str) -> Vec<PathOp<'_>> {
    let mut ops = Vec::new();
    for part in path.split('.') {
        match first_unescaped(part, b'[') {
            Some(bpos) if parse_index_groups(&part[bpos..]).is_some() => {
                let name = &part[..bpos];
                if !name.is_empty() {
                    ops.push(PathOp::Key(unescape_brackets(name)));
                }
                for idx in parse_index_groups(&part[bpos..]).expect("checked") {
                    ops.push(PathOp::Index(idx));
                }
            }
            // No unescaped index group: the whole segment is a literal key,
            // with `\[` / `\]` unescaped to match the event's actual key.
            _ => ops.push(PathOp::Key(unescape_brackets(part))),
        }
    }
    ops
}

/// Parse `[N]` or `[N][M]...` into the contained indices (negative allowed), or
/// `None` if any group is malformed or non-numeric.
fn parse_index_groups(s: &str) -> Option<Vec<i64>> {
    let mut out = Vec::new();
    let mut rem = s;
    while !rem.is_empty() {
        let rest = rem.strip_prefix('[')?;
        let close = rest.find(']')?;
        let idx: i64 = rest[..close].parse().ok()?;
        out.push(idx);
        rem = &rest[close + 1..];
    }
    Some(out)
}

/// Follow navigation ops, collecting every terminal value into `out`.
///
/// A `Key` op distributes over arrays (implicit any-member): the remaining ops
/// are applied to every element, so a path crossing an array of objects yields
/// one value per element. An `Index` op selects a single element and never
/// fans out, giving deterministic positional access.
fn collect_by_ops<'a>(current: &'a Value, ops: &[PathOp<'_>], out: &mut Vec<EventValue<'a>>) {
    let Some((op, rest)) = ops.split_first() else {
        out.push(EventValue::from(current));
        return;
    };

    match op {
        PathOp::Key(key) => match current {
            Value::Object(map) => {
                if let Some(next) = map.get(key.as_ref()) {
                    collect_by_ops(next, rest, out);
                }
            }
            Value::Array(arr) => {
                for item in arr {
                    collect_by_ops(item, ops, out);
                }
            }
            _ => {}
        },
        PathOp::Index(i) => {
            if let Value::Array(arr) = current
                && let Some(idx) = resolve_array_index(*i, arr.len())
                && let Some(next) = arr.get(idx)
            {
                collect_by_ops(next, rest, out);
            }
        }
    }
}

fn any_string_value_json(v: &Value, pred: &dyn Fn(&str) -> bool, depth: usize) -> bool {
    if depth == 0 {
        return false;
    }
    match v {
        Value::String(s) => pred(s.as_str()),
        Value::Object(map) => map
            .values()
            .any(|val| any_string_value_json(val, pred, depth - 1)),
        Value::Array(arr) => arr
            .iter()
            .any(|val| any_string_value_json(val, pred, depth - 1)),
        _ => false,
    }
}

fn collect_field_keys<'a>(v: &'a Value, prefix: &str, out: &mut Vec<Cow<'a, str>>, depth: usize) {
    if depth == 0 {
        return;
    }
    if let Value::Object(map) = v {
        for (k, child) in map {
            let path = if prefix.is_empty() {
                k.clone()
            } else {
                format!("{prefix}.{k}")
            };
            match child {
                // Recurse into nested objects but do NOT emit the
                // intermediate path; only the leaf descendants count.
                // Sigma rules normally reference leaves via
                // dot-notation, so emitting `actor` alongside
                // `actor.id` would falsely flag the parent as
                // "unknown" in the gap signal.
                Value::Object(_) => collect_field_keys(child, &path, out, depth - 1),
                _ => out.push(Cow::Owned(path)),
            }
        }
    }
}

fn collect_string_values_json<'a>(v: &'a Value, out: &mut Vec<Cow<'a, str>>, depth: usize) {
    if depth == 0 {
        return;
    }
    match v {
        Value::String(s) => out.push(Cow::Borrowed(s.as_str())),
        Value::Object(map) => {
            for val in map.values() {
                collect_string_values_json(val, out, depth - 1);
            }
        }
        Value::Array(arr) => {
            for val in arr {
                collect_string_values_json(val, out, depth - 1);
            }
        }
        _ => {}
    }
}

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

    #[test]
    fn json_flat_field() {
        let v = json!({"CommandLine": "whoami", "User": "admin"});
        let event = JsonEvent::borrow(&v);
        assert_eq!(
            event.get_field("CommandLine"),
            Some(EventValue::Str(Cow::Borrowed("whoami")))
        );
    }

    #[test]
    fn json_nested_field() {
        let v = json!({"actor": {"id": "user123", "type": "User"}});
        let event = JsonEvent::borrow(&v);
        assert_eq!(
            event.get_field("actor.id"),
            Some(EventValue::Str(Cow::Borrowed("user123")))
        );
    }

    #[test]
    fn json_flat_key_precedence() {
        let v = json!({"actor.id": "flat_value", "actor": {"id": "nested_value"}});
        let event = JsonEvent::borrow(&v);
        assert_eq!(
            event.get_field("actor.id"),
            Some(EventValue::Str(Cow::Borrowed("flat_value")))
        );
    }

    #[test]
    fn json_missing_field() {
        let v = json!({"foo": "bar"});
        let event = JsonEvent::borrow(&v);
        assert_eq!(event.get_field("missing"), None);
    }

    #[test]
    fn json_null_field() {
        let v = json!({"foo": null});
        let event = JsonEvent::borrow(&v);
        assert_eq!(event.get_field("foo"), Some(EventValue::Null));
    }

    #[test]
    fn json_array_traversal() {
        // A path crossing an array of objects now collects every element's
        // leaf value (any-member semantics), not just the first.
        let v = json!({"a": {"b": [{"c": "found"}, {"c": "other"}]}});
        let event = JsonEvent::borrow(&v);
        assert_eq!(
            event.get_field("a.b.c"),
            Some(EventValue::Array(vec![
                EventValue::Str(Cow::Borrowed("found")),
                EventValue::Str(Cow::Borrowed("other")),
            ]))
        );
    }

    #[test]
    fn json_array_traversal_no_match() {
        let v = json!({"a": {"b": [{"x": 1}, {"y": 2}]}});
        let event = JsonEvent::borrow(&v);
        assert_eq!(event.get_field("a.b.c"), None);
    }

    #[test]
    fn json_array_traversal_deep() {
        let v = json!({
            "events": [
                {"actors": [{"name": "alice"}, {"name": "bob"}]},
                {"actors": [{"name": "charlie"}]}
            ]
        });
        let event = JsonEvent::borrow(&v);
        // Nested arrays flatten to every leaf value.
        assert_eq!(
            event.get_field("events.actors.name"),
            Some(EventValue::Array(vec![
                EventValue::Str(Cow::Borrowed("alice")),
                EventValue::Str(Cow::Borrowed("bob")),
                EventValue::Str(Cow::Borrowed("charlie")),
            ]))
        );
    }

    #[test]
    fn json_array_at_root_level() {
        let v = json!({"process": [{"command_line": "whoami"}, {"command_line": "id"}]});
        let event = JsonEvent::borrow(&v);
        assert_eq!(
            event.get_field("process.command_line"),
            Some(EventValue::Array(vec![
                EventValue::Str(Cow::Borrowed("whoami")),
                EventValue::Str(Cow::Borrowed("id")),
            ]))
        );
    }

    #[test]
    fn json_array_returns_array_value() {
        let v = json!({"a": {"tags": ["t1", "t2"]}});
        let event = JsonEvent::borrow(&v);
        let result = event.get_field("a.tags");
        assert!(matches!(result, Some(EventValue::Array(_))));
    }

    #[test]
    fn json_flat_key_wins_over_array_traversal() {
        let v = json!({"a.b.c": "flat", "a": {"b": [{"c": "nested"}]}});
        let event = JsonEvent::borrow(&v);
        assert_eq!(
            event.get_field("a.b.c"),
            Some(EventValue::Str(Cow::Borrowed("flat")))
        );
    }

    #[test]
    fn json_all_string_values() {
        let v = json!({
            "a": "hello",
            "b": 42,
            "c": {"d": "world", "e": true},
            "f": ["one", "two"]
        });
        let event = JsonEvent::borrow(&v);
        let values = event.all_string_values();
        let strs: Vec<&str> = values.iter().map(|c| c.as_ref()).collect();
        assert!(strs.contains(&"hello"));
        assert!(strs.contains(&"world"));
        assert!(strs.contains(&"one"));
        assert!(strs.contains(&"two"));
        assert_eq!(values.len(), 4);
    }

    #[test]
    fn json_to_json_roundtrip() {
        let v = json!({"a": 1, "b": "hello", "c": [1, 2]});
        let event = JsonEvent::borrow(&v);
        assert_eq!(event.to_json(), v);
    }

    #[test]
    fn json_owned_works() {
        let v = json!({"key": "value"});
        let event = JsonEvent::owned(v.clone());
        assert_eq!(
            event.get_field("key"),
            Some(EventValue::Str(Cow::Borrowed("value")))
        );
        assert_eq!(event.to_json(), v);
    }

    #[test]
    fn json_field_keys_flat() {
        let v = json!({"CommandLine": "x", "User": "y"});
        let event = JsonEvent::borrow(&v);
        let mut keys: Vec<String> = event.field_keys().iter().map(|c| c.to_string()).collect();
        keys.sort();
        assert_eq!(keys, vec!["CommandLine", "User"]);
    }

    #[test]
    fn json_field_keys_nested_leaves_only() {
        // Intermediate object names like `actor` are NOT emitted; only
        // leaves (`actor.id`, `actor.type`) and top-level scalars
        // (`verb`) appear.
        let v = json!({"actor": {"id": "u1", "type": "User"}, "verb": "login"});
        let event = JsonEvent::borrow(&v);
        let mut keys: Vec<String> = event.field_keys().iter().map(|c| c.to_string()).collect();
        keys.sort();
        assert_eq!(keys, vec!["actor.id", "actor.type", "verb"]);
    }

    #[test]
    fn json_field_keys_deeply_nested_leaves_only() {
        let v = json!({"a": {"b": {"c": 1}}, "flat": "x"});
        let event = JsonEvent::borrow(&v);
        let mut keys: Vec<String> = event.field_keys().iter().map(|c| c.to_string()).collect();
        keys.sort();
        assert_eq!(keys, vec!["a.b.c", "flat"]);
    }

    #[test]
    fn json_field_keys_array_parent_only() {
        let v = json!({"events": [{"id": 1}, {"id": 2}]});
        let event = JsonEvent::borrow(&v);
        let keys: Vec<String> = event.field_keys().iter().map(|c| c.to_string()).collect();
        // Arrays contribute their parent key only; array indices are not enumerated.
        assert_eq!(keys, vec!["events"]);
    }

    #[test]
    fn json_field_keys_top_level_non_object_empty() {
        let v = json!("just a string");
        let event = JsonEvent::owned(v);
        assert!(event.field_keys().is_empty());
    }

    #[test]
    fn json_traversal_with_consecutive_dots_does_not_panic() {
        // Pathological input -- a path like `a..b` used to be tokenised
        // by `split('.')` into `["a", "", "b"]` and then walked head-by-
        // head; the new `split_once('.')` recursion produces the same
        // `("a", ".b")` -> `("", "b")` -> ... sequence with no
        // allocation. Verify the lookup falls back to `None` rather
        // than panicking or accidentally matching.
        let v = json!({"a": {"b": "x"}});
        let event = JsonEvent::borrow(&v);
        assert_eq!(event.get_field("a..b"), None);
    }

    #[test]
    fn json_traversal_with_trailing_dot_does_not_panic() {
        // A trailing dot used to leave an empty trailing segment in the
        // `Vec<&str>` path which the object branch tried to look up
        // against the map; the iterator-based walker preserves that
        // miss without allocating.
        let v = json!({"a": {"b": "x"}});
        let event = JsonEvent::borrow(&v);
        assert_eq!(event.get_field("a.b."), None);
    }
}
