// Copyright (c) 2026, Salesforce, Inc.,
// All rights reserved.
// For full license text, see the LICENSE.txt file

//! Expression types for the Policy Expression Language (PEL).
//!
//! This module defines the abstract syntax tree (AST) for PEL expressions, including
//! the various expression types and their components. These types are used to represent
//! parsed DataWeave expressions in a way that can be evaluated by the PEL runtime.

use std::rc::Rc;

use crate::{runtime::value::Value, Location};

macro_rules! impl_from {
    ($into:ty => $($from:ty as $variant:ident),*) => {
        $(impl From<$from> for $into {
            fn from(value: $from) -> Self {
                Self::$variant(value)
            }
        })*
    };

    ($into:ty => $($from:ident),*) => {
        impl_from! {$into => $($from as $from),*}
    };
}

/// Represents a complete PEL expression with source location information.
///
/// Every expression in PEL has a location in the source code and a body that
/// defines its structure and behavior.
#[derive(Clone, Debug, PartialEq)]
pub struct Expression {
    /// The source location of this expression, used for error reporting.
    pub location: Location,

    /// The actual expression content and structure.
    pub body: Body,
}

/// The different kinds of expression bodies that can appear in PEL.
///
/// This enum represents all possible expression types in the PEL abstract syntax tree.
#[derive(Clone, Debug, PartialEq)]
pub enum Body {
    /// A reference to a variable or function name.
    Ref(Ref),

    /// A function application.
    Apply(Apply),

    /// An array literal containing zero or more expressions.
    Array(Vec<Expression>),

    /// A default operator expression (e.g., `a default b`).
    DefaultOperator(DefaultOperator),

    /// A selection expression (e.g., `object.field` or `array[0]`).
    Selection(Selection),

    /// An attribute access expression.
    Attribute(Attribute),

    /// A multi-selection expression for handling multiple fields at once.
    MultiSelect(MultiSelect),

    /// A conditional expression (if-then-else).
    IfElse(IfElse),

    /// A unary operation (e.g., `-x` or `!x`).
    UnaryOperation(UnaryOperation),

    /// A binary operation (e.g., `a + b` or `x > y`).
    Operation(Operation),

    /// A literal value (string, number, boolean, etc.).
    Value(Value),
}

impl_from! {
    Body =>
        Ref,
        Apply,
        Selection,
        Attribute,
        MultiSelect,
        IfElse,
        DefaultOperator,
        UnaryOperation,
        Operation,
        Value
}

impl_from! {
    Body => Vec<Expression> as Array
}

impl Body {
    pub(crate) fn as_value(&self) -> Option<&Value> {
        match self {
            Body::Value(v) => Some(v),
            _ => None,
        }
    }
}

impl Expression {
    pub fn new(location: Location, body: impl Into<Body>) -> Self {
        Self {
            location,
            body: body.into(),
        }
    }
}

#[derive(Clone, Eq, PartialEq, Hash, Debug)]
/// The token representing a name.
pub struct Symbol(Rc<str>);

impl Symbol {
    pub fn new(value: impl Into<Rc<str>>) -> Self {
        Self(value.into())
    }

    pub fn as_str(&self) -> &str {
        self.0.as_ref()
    }
}

/// A reference to a variable or function name in an expression.
///
/// This represents a named reference that will be resolved during evaluation.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Ref(pub Symbol);

/// A selection expression that accesses a property or element.
///
/// This represents expressions like `object.field` or `array[index]`.
#[derive(Clone, Debug, PartialEq)]
pub struct Selection {
    /// The target expression being selected from.
    pub target: Box<Expression>,

    /// The selector expression that identifies what to select.
    pub selector: Box<Expression>,
}

/// An attribute access expression.
///
/// Represents accessing a named attribute of an object or module.
#[derive(Clone, Debug, PartialEq)]
pub struct Attribute {
    /// The target expression whose attribute is being accessed.
    pub target: Box<Expression>,

    /// The selector expression that identifies what to select.
    pub selector: Box<Expression>,
}

/// A multi-selection expression that selects multiple fields at once.
///
/// This is used for expressions that need to extract multiple fields from
/// an object or elements from an array in a single operation.
#[derive(Clone, Debug, PartialEq)]
pub struct MultiSelect {
    /// The target expression to select from.
    pub target: Box<Expression>,

    /// The selector expression that identifies what to select.
    pub selector: Box<Expression>,
}

/// A function application expression.
///
/// Represents calling a function with a list of argument expressions.
#[derive(Clone, Debug, PartialEq)]
pub struct Apply {
    /// The expression of the function to apply.
    pub function: Box<Expression>,
    /// The arguments to provide to the function.
    pub arguments: Vec<Expression>,
}

/// A default operator expression.
///
/// Represents expressions of the form `left default right`, which evaluates to
/// `left` if it's not `null`, otherwise evaluates to `right`.
#[derive(Clone, Debug, PartialEq)]
pub struct DefaultOperator {
    /// The left-hand side expression to check.
    pub left: Box<Expression>,

    /// The fallback expression to use if left is `null`.
    pub right: Box<Expression>,
}

/// A conditional (if-then-else) expression.
///
/// Represents expressions of the form `if (condition) then_branch else else_branch`.
#[derive(Clone, Debug, PartialEq)]
pub struct IfElse {
    /// The condition to test.
    pub condition: Box<Expression>,

    /// The expression to evaluate if the condition is true.
    pub true_branch: Box<Expression>,

    /// The expression to evaluate if the condition is false.
    pub false_branch: Box<Expression>,
}

/// A binary operation expression.
///
/// Represents operations like addition, comparison, logical operations, etc.
#[derive(Clone, Debug, PartialEq)]
pub struct Operation {
    /// The operation to perform.
    pub operator: Operator,
    /// The left-hand operand.
    pub left: Box<Expression>,
    /// The right-hand operand.
    pub right: Box<Expression>,
}

/// Binary operators supported in PEL expressions.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Operator {
    /// Equality (`==`)
    Eq,

    /// Inequality (`!=`)
    Neq,

    /// Less Than (`<`)
    Lt,

    /// Greater Than (`>`)
    Gt,

    /// Less than or equal (`<=`)
    Let,

    /// Greater than or equal (`>=`)
    Get,

    /// Logical AND (`&&`)
    And,

    /// Logical OR (`||`)
    Or,
}

/// Represents operations that take a single operand.
#[derive(Clone, Debug, PartialEq)]
pub struct UnaryOperation {
    /// The unary operator to apply.
    pub operator: UnaryOperator,
    /// The operand to apply the operator to.
    pub operand: Box<Expression>,
}

/// Unary operators supported in PEL expressions.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum UnaryOperator {
    /// Logical NOT (`!`)
    Not,
}
