use crate::parser::model::{
    Comparable, Filter, FilterAtom, FnArg, Literal, Segment, Selector, SingularQuery, Test,
};

#[macro_export]
macro_rules! lit {
    () => {
        Literal::Null
    };
    (b$b:expr ) => {
        Literal::Bool($b)
    };
    (s$s:expr) => {
        Literal::String($s.to_string())
    };
    (i$n:expr) => {
        Literal::Int($n)
    };
    (f$n:expr) => {
        Literal::Float($n)
    };
}

#[macro_export]
macro_rules! q_segments {
    ($segment:tt) => {
        vec![q_segment!($segment)]
    };
    // Recursive case: multiple segments
    ($segment:tt $($rest:tt)*) => {{
        let mut segments = q_segments!($($rest)*);
        segments.insert(0, q_segment!($segment));
        segments
    }};
}

#[macro_export]
macro_rules! q_segment {
    ($name:ident) => {
        SingularQuerySegment::Name(stringify!($name).to_string())
    };
    ([$name:ident]) => {
        SingularQuerySegment::Name(format!("\"{}\"", stringify!($name)))
    };
    ([$index:expr]) => {
        SingularQuerySegment::Index($index)
    };
}
#[macro_export]
macro_rules! singular_query {
    (@ $($segment:tt)*) => {
        SingularQuery::Current(q_segments!($($segment)*))
    };
    ($($segment:tt)*) => {
        SingularQuery::Root(q_segments!($($segment)*))
    };
}

#[macro_export]
macro_rules! slice {
    () => {
        (None, None, None)
    };
    ($start:expr) => {
        (Some($start), None, None)
    };
    ($start:expr, $end:expr) => {
        (Some($start), Some($end), None)
    };
    ($start:expr,, $step:expr) => {
        (Some($start), None, Some($step))
    };
    (,, $step:expr) => {
        (None, None, Some($step))
    };
    (, $end:expr) => {
        (None, Some($end), None)
    };
    (, $end:expr,$step:expr ) => {
        (None, Some($end), Some($step))
    };
    ($start:expr, $end:expr, $step:expr) => {
        (Some($start), Some($end), Some($step))
    };
}

#[macro_export]
macro_rules! test_fn {
    ($name:ident $arg:expr) => {
        TestFunction::try_new(stringify!($name), vec![$arg]).unwrap()
    };

    ($name:ident $arg1:expr, $arg2:expr ) => {
        TestFunction::try_new(stringify!($name), vec![$arg1, $arg2]).unwrap()
    };
}

#[macro_export]
macro_rules! arg {
    ($arg:expr) => {
        FnArg::Literal($arg)
    };
    (t $arg:expr) => {
        FnArg::Test(Box::new($arg))
    };
    (f $arg:expr) => {
        FnArg::Filter($arg)
    };
}

#[macro_export]
macro_rules! test {
  (@ $($segments:expr)*) => { Test::RelQuery(vec![$($segments),*]) };
  (S $jq:expr) => { Test::AbsQuery($jq) };
  ($tf:expr) => { Test::Function(Box::new($tf)) };

}

#[macro_export]
macro_rules! or {
    ($($items:expr),*) => {
       crate::parser::model::Filter::Or(vec![ $($items),* ])
    };
}

#[macro_export]
macro_rules! and {
    ($($items:expr),*) => {
        crate::parser::model::Filter::And(vec![ $($items),* ])
    };
}

#[macro_export]
macro_rules! filter_ {
    ($item:expr) => {
        crate::parser::model::Filter::Atom($item)
    };

    (or $($items:expr),*) => {
       crate::parser::model::Filter::Or(vec![ $($items),* ])
    };

    (and $($items:expr),*) => {
        crate::parser::model::Filter::And(vec![ $($items),* ])
    };
}

#[macro_export]
macro_rules! atom {
    (! $filter:expr) => {
        FilterAtom::filter($filter, true)
    };
    ($filter:expr) => {
        FilterAtom::filter($filter, false)
    };
    (t! $filter:expr) => {
        FilterAtom::test($filter, true)
    };
    (t $filter:expr) => {
        FilterAtom::filter($filter, false)
    };
    ($lhs:expr, $s:expr, $rhs:expr) => {
        FilterAtom::Comparison(Box::new(cmp!($lhs, $s, $rhs)))
    };
}

#[macro_export]
macro_rules! cmp {
    ($lhs:expr, $op:expr , $rhs:expr) => {
        Comparison::try_new($op, $lhs, $rhs).unwrap()
    };
}

#[macro_export]
macro_rules! comparable {
    ($lit:expr) => {
        Comparable::Literal($lit)
    };
    (f $func:expr) => {
        Comparable::Function($func)
    };
    (> $sq:expr) => {
        Comparable::SingularQuery($sq)
    };
}

#[macro_export]
macro_rules! selector {
    (*) => {
        Selector::Wildcard
    };
    (?$filter:expr) => {
        Selector::Filter($filter)
    };
    (slice $slice:expr) => {
        slice_from($slice)
    };
    ($name:ident) => {
        Selector::Name(stringify!($name).to_string())
    };
    ([$name:ident]) => {
        Selector::Name(format!("\"{}\"", stringify!($name)))
    };
    ([$index:expr]) => {
        Selector::Index($index)
    };
}

#[macro_export]
macro_rules! segment {
    (..$segment:expr) => {
        Segment::Descendant(Box::new($segment))
    };
    ($selector:expr) => {
        Segment::Selector($selector)
    };
    ($($selectors:expr),*) => {
        Segment::Selectors(vec![$($selectors),*])
    };
}

#[macro_export]
macro_rules! jq {
    ($($segment:expr),*) => {
        JpQuery::new(vec![$($segment),*])
    };
}
