mod command;
mod error;
mod formatting;
mod predicates;
mod script_result;
mod stream_message;
mod token;
mod value;
mod variables;

use std::collections::HashMap;

pub(crate) use command::CommandError;
use command::{Command, CommandElem};
pub(crate) use stream_message::StreamMessage;
use value::{
    ClassProperties, ClassType, Convert, Encoding, MethodName, Param, RuntimeObjectTrait,
    RuntimeTypeTrait, ScriptBlock, ValResult,
};
use variables::{Scope, SessionScope, TopScope};
type ParserResult<T> = core::result::Result<T, ParserError>;
use error::ParserError;
type PestError = pest::error::Error<Rule>;
use pest::Parser;
use pest_derive::Parser;
use predicates::{ArithmeticPred, BitwisePred, LogicalPred, StringPred};
pub use script_result::{PsValue, ScriptResult};
pub use token::{CommandToken, ExpressionToken, MethodToken, StringExpandableToken, Token, Tokens};
pub(crate) use value::Val;
use value::ValType;
pub use variables::Variables;
use variables::{VarName, VariableError};

use crate::parser::{command::CommandOutput, value::RuntimeError};

type Pair<'i> = ::pest::iterators::Pair<'i, Rule>;
type Pairs<'i> = ::pest::iterators::Pairs<'i, Rule>;

trait IteratorExt: Iterator {
    fn try_next(&mut self) -> Result<Self::Item, ParserError> {
        self.next().ok_or(ParserError::NotImplemented(
            "unexpected end of tokens".into(),
        ))
    }
}
impl<T: Iterator> IteratorExt for T {}

pub(crate) const NEWLINE: &str = "\n";

macro_rules! unexpected_token {
    ($pair:expr) => {
        panic!("Unexpected token: {:?}", $pair.as_rule())
    };
}

macro_rules! check_rule {
    ($pair:expr, $rule:pat) => {
        if !matches!($pair.as_rule(), $rule) {
            panic!(
                "Unexpected token: {:?}, instead of {}",
                $pair.as_rule(),
                stringify!($rule)
            );
        }
    };
}

macro_rules! not_implemented {
    ($token:expr) => {
        Err(ParserError::NotImplemented(format!(
            "Not implemented: {:?}",
            $token.as_rule()
        )))
    };
}

#[derive(Default, Clone)]
pub(crate) struct Results {
    output: Vec<StreamMessage>,
    deobfuscated: Vec<String>,
}

impl Results {
    fn new() -> Self {
        Self {
            output: Vec::new(),
            deobfuscated: Vec::new(),
        }
    }
}

#[derive(Parser)]
#[grammar = "powershell.pest"]
pub struct PowerShellSession {
    variables: Variables,
    tokens: Tokens,
    errors: Vec<ParserError>,
    results: Vec<Results>,
    skip_error: u32,
    default_scope: TopScope,
    types_map: HashMap<String, Box<dyn RuntimeTypeTrait>>,
}

impl Clone for PowerShellSession {
    fn clone(&self) -> Self {
        Self {
            variables: self.variables.clone(),
            ..Default::default()
        }
    }
}

impl Default for PowerShellSession {
    fn default() -> Self {
        Self::new()
    }
}
const CONVERT: Convert = Convert {};
const ENCODING: Encoding = Encoding {};
impl<'a> PowerShellSession {
    /// Creates a new PowerShell parsing session with default settings.
    ///
    /// The session is initialized with built-in variables like `$true`,
    /// `$false`, `$null`, and special variables like `$?` for error status
    /// tracking.
    ///
    /// # Returns
    ///
    /// A new `PowerShellSession` instance ready for script evaluation.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use ps_parser::PowerShellSession;
    ///
    /// let mut session = PowerShellSession::new();
    /// let result = session.safe_eval("$true").unwrap();
    /// assert_eq!(result, "True");
    /// ```
    pub fn new() -> Self {
        let mut types_map = HashMap::new();
        types_map.insert(
            CONVERT.full_name().to_ascii_lowercase(),
            Box::new(CONVERT) as _,
        );
        types_map.insert(
            ENCODING.full_name().to_ascii_lowercase(),
            Box::new(ENCODING) as _,
        );

        Self {
            variables: Variables::new(),
            tokens: Tokens::new(),
            errors: Vec::new(),
            results: Vec::new(),
            skip_error: 0,
            default_scope: TopScope::Session,
            types_map,
        }
    }

    /// Creates a new PowerShell session with the provided variables.
    ///
    /// This constructor allows you to initialize the session with a custom set
    /// of variables, such as environment variables or variables loaded from
    /// configuration files.
    ///
    /// # Arguments
    ///
    /// * `variables` - A `Variables` instance containing the initial variable
    ///   set.
    ///
    /// # Returns
    ///
    /// A new `PowerShellSession` instance with the provided variables.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use ps_parser::{PowerShellSession, Variables};
    ///
    /// let env_vars = Variables::env();
    /// let mut session = PowerShellSession::new().with_variables(env_vars);
    /// let username = session.safe_eval("$env:USERNAME").unwrap();
    /// ```
    pub fn with_variables(mut self, variables: Variables) -> Self {
        self.variables = variables;
        self
    }

    /// Safely evaluates a PowerShell script and returns the output as a string.
    ///
    /// This method parses and evaluates the provided PowerShell script,
    /// handling errors gracefully and returning the result as a formatted
    /// string. It's the recommended method for simple script evaluation.
    ///
    /// # Arguments
    ///
    /// * `script` - A string slice containing the PowerShell script to
    ///   evaluate.
    ///
    /// # Returns
    ///
    /// * `Result<String, ParserError>` - The output of the script evaluation,
    ///   or an error if parsing/evaluation fails.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use ps_parser::PowerShellSession;
    ///
    /// let mut session = PowerShellSession::new();
    ///
    /// // Simple arithmetic
    /// let result = session.safe_eval("1 + 2 * 3").unwrap();
    /// assert_eq!(result, "7");
    ///
    /// // Variable assignment and retrieval
    /// let result = session.safe_eval("$name = 'World'; \"Hello $name\"").unwrap();
    /// assert_eq!(result, "Hello World");
    /// ```
    pub fn safe_eval(&mut self, script: &str) -> Result<String, ParserError> {
        let script_res = self.parse_command(script)?;
        Ok(script_res.result().to_string())
    }

    pub fn deobfuscate_script(&mut self, script: &str) -> Result<String, ParserError> {
        self.push_scope_session();
        let script_res = self.parse_script(script)?;
        self.pop_scope_session();
        Ok(script_res.deobfuscated().to_string())
    }

    pub fn env_variables(&self) -> HashMap<String, PsValue> {
        self.variables
            .get_env()
            .into_iter()
            .map(|(k, v)| (k, v.into()))
            .collect()
    }

    pub fn session_variables(&self) -> HashMap<String, PsValue> {
        self.variables
            .get_global()
            .into_iter()
            .map(|(k, v)| (k, v.into()))
            .collect()
    }

    /// Parses and evaluates a PowerShell script, returning detailed results.
    ///
    /// This method provides comprehensive information about the parsing and
    /// evaluation process, including the final result, generated output,
    /// any errors encountered, and the tokenized representation of the
    /// script. It's particularly useful for debugging and deobfuscation.
    ///
    /// # Arguments
    ///
    /// * `input` - A string slice containing the PowerShell script to parse and
    ///   evaluate.
    ///
    /// # Returns
    ///
    /// * `Result<ScriptResult, ParserError>` - A detailed result containing the
    ///   evaluation outcome, output, errors, and tokens, or a parsing error if
    ///   the script is malformed.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use ps_parser::PowerShellSession;
    ///
    /// let mut session = PowerShellSession::new();
    /// let script_result = session.parse_command("$a = 42; Write-Output $a").unwrap();
    ///
    /// println!("Final result: {:?}", script_result.result());
    /// println!("Generated output: {:?}", script_result.output());
    /// println!("Parsing errors: {:?}", script_result.errors());
    /// println!("Deobfuscated code: {:?}", script_result.deobfuscated());
    /// ```
    pub fn parse_script(&mut self, input: &str) -> Result<ScriptResult, ParserError> {
        self.default_scope = TopScope::Script;
        self.variables.init(self.default_scope.clone());

        let (script_last_output, mut result) = self.parse_subscript(input)?;
        self.variables.clear_script_functions();
        Ok(ScriptResult::new(
            script_last_output,
            std::mem::take(&mut result.output),
            std::mem::take(&mut result.deobfuscated),
            std::mem::take(&mut self.tokens),
            std::mem::take(&mut self.errors),
            self.variables
                .script_scope()
                .into_iter()
                .map(|(k, v)| (k, v.into()))
                .collect(),
        ))
    }

    pub fn parse_command(&mut self, input: &str) -> Result<ScriptResult, ParserError> {
        self.default_scope = TopScope::Session;
        self.variables.init(self.default_scope.clone());

        let (script_last_output, mut result) = self.parse_subscript(input)?;
        self.variables.clear_script_functions();
        Ok(ScriptResult::new(
            script_last_output,
            std::mem::take(&mut result.output),
            std::mem::take(&mut result.deobfuscated),
            std::mem::take(&mut self.tokens),
            std::mem::take(&mut self.errors),
            self.variables
                .script_scope()
                .into_iter()
                .map(|(k, v)| (k, v.into()))
                .collect(),
        ))
    }

    pub(crate) fn parse_subscript(&mut self, input: &str) -> Result<(Val, Results), ParserError> {
        let mut pairs = PowerShellSession::parse(Rule::program, input)?;
        //create new scope for script
        self.results.push(Results::new());

        let program_token = pairs.next().expect("");

        let mut script_last_output = Val::default();

        if let Rule::program = program_token.as_rule() {
            let mut pairs = program_token.into_inner();
            let _script_param_block_token = pairs.try_next()?;
            if let Some(named_blocks) = pairs.peek()
                && named_blocks.as_rule() == Rule::named_blocks
            {
                //todo
                let _ = pairs.next();
            }
            for token in pairs {
                let token_str = token.as_str();
                match token.as_rule() {
                    Rule::statement_terminator => continue,
                    Rule::EOI => break,
                    _ => {}
                };

                let result = self.eval_statement(token.clone());
                self.variables.set_status(result.is_ok());

                if let Ok(Val::NonDisplayed(_)) = &result {
                    continue;
                }

                script_last_output = match result {
                    Ok(val) => {
                        if val != Val::Null {
                            self.add_output_statement(val.display().into());
                            self.add_deobfuscated_statement(val.cast_to_script());
                        }

                        val
                    }
                    Err(e) => {
                        self.errors.push(e);
                        self.add_deobfuscated_statement(token_str.into());
                        Val::Null
                    }
                };
            }
        }

        Ok((script_last_output, self.results.pop().unwrap_or_default()))
    }

    fn add_function(
        &mut self,
        name: String,
        func: ScriptBlock,
        scope: TopScope,
    ) -> ParserResult<Val> {
        // let func_str= func.to_function(&name, &scope);
        // self.add_deobfuscated_statement(func_str);

        if let TopScope::Session = scope {
            self.variables.add_global_function(name.clone(), func);
        } else {
            self.variables.add_script_function(name.clone(), func);
        }

        Err(ParserError::Skip)
    }

    pub(crate) fn parse_function_statement(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        check_rule!(token, Rule::function_statement);

        let mut pair = token.into_inner();

        let function_keyword_token = pair.try_next()?;
        check_rule!(function_keyword_token, Rule::function_keyword);

        let mut next_token = pair.try_next()?;
        let scope: TopScope = if next_token.as_rule() == Rule::scope_keyword {
            let scope = Scope::from(next_token.as_str());
            next_token = pair.try_next()?;
            scope.into()
        } else {
            self.default_scope.clone()
        };

        let function_name_token = next_token;
        check_rule!(function_name_token, Rule::function_name);
        let fname = function_name_token.as_str().to_ascii_lowercase();

        let Some(mut next_token) = pair.next() else {
            //empty function
            return self.add_function(fname, ScriptBlock::empty(), scope);
        };

        let params = if next_token.as_rule() == Rule::parameter_list {
            let param_list = self.parse_parameter_list(next_token)?;
            if let Some(token) = pair.next() {
                next_token = token;
            } else {
                return self.add_function(fname, ScriptBlock::empty(), scope);
            }

            param_list
        } else {
            Vec::new()
        };
        check_rule!(next_token, Rule::script_block);

        let mut script_block = self.parse_script_block(next_token)?;

        if script_block.params.0.is_empty() {
            script_block = script_block.with_params(params);
        }

        self.add_function(fname, script_block, scope)
    }

    pub(crate) fn eval_if_statement(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        check_rule!(token, Rule::if_statement);

        //collect tokens from each case
        self.if_statement_collect_tokens(token.clone());

        let mut pair = token.into_inner();
        let condition_token = pair.try_next()?;
        let true_token = pair.try_next()?;
        let condition_val = self.eval_pipeline(condition_token.clone())?;
        let res = if condition_val.cast_to_bool() {
            self.eval_statement_block(true_token)?
        } else if let Some(mut token) = pair.next() {
            if token.as_rule() == Rule::elseif_clauses {
                for else_if in token.into_inner() {
                    let mut pairs = else_if.into_inner();
                    let condition_token = pairs.try_next()?;
                    let statement_token = pairs.try_next()?;
                    let condition_val = self.eval_pipeline(condition_token)?;
                    if condition_val.cast_to_bool() {
                        return self.eval_statement_block(statement_token);
                    }
                }
                let Some(token2) = pair.next() else {
                    return Ok(Val::Null);
                };
                token = token2;
            }
            if token.as_rule() == Rule::else_condition {
                let statement_token = token.into_inner().try_next()?;
                self.eval_statement_block(statement_token)?
            } else {
                Val::Null
            }
        } else {
            Val::Null
        };

        Ok(res)
    }

    pub(crate) fn if_statement_collect_tokens(&mut self, token: Pair<'a>) {
        //we want collect tokens from each case, but we need to preserve all variables
        //to consider: maybe instead of collecting tokens, we should return whole
        // deobfuscated if statement
        let results = self.results.clone();
        let current_variables = self.variables.clone();
        if let Err(err) = self.impl_if_statement_collect_tokens(token.clone()) {
            log::debug!("Error during if_statement_collect_tokens: {:?}", err);
        }
        self.variables = current_variables;
        self.results = results;
    }

    pub(crate) fn impl_if_statement_collect_tokens(&mut self, token: Pair<'a>) -> ParserResult<()> {
        check_rule!(token, Rule::if_statement);

        let mut pair = token.into_inner();
        let condition_token = pair.try_next()?;
        let true_token = pair.try_next()?;
        let _condition_val = self
            .eval_pipeline(condition_token.clone())
            .unwrap_or_default();
        if let Err(err) = self.eval_statement_block(true_token) {
            log::debug!(
                "Error during if_statement_collect_tokens (true block): {:?}",
                err
            );
        }
        if let Some(mut token) = pair.next() {
            if token.as_rule() == Rule::elseif_clauses {
                for else_if in token.into_inner() {
                    let mut pairs = else_if.into_inner();
                    let condition_token = pairs.try_next()?;
                    let statement_token = pairs.try_next()?;
                    let _condition_val = self.eval_pipeline(condition_token).unwrap_or_default();

                    if let Err(err) = self.eval_statement_block(statement_token) {
                        log::debug!(
                            "Error during if_statement_collect_tokens (else if block): {:?}",
                            err
                        );
                    }
                }
                let Some(token2) = pair.next() else {
                    return Ok(());
                };
                token = token2;
            }
            if token.as_rule() == Rule::else_condition {
                let statement_token = token.into_inner().try_next()?;
                if let Err(err) = self.eval_statement_block(statement_token) {
                    log::debug!(
                        "Error during if_statement_collect_tokens (else block): {:?}",
                        err
                    );
                }
            }
        }
        Ok(())
    }

    pub(crate) fn statement_block_collect_tokens(&mut self, token: Pair<'a>) {
        check_rule!(token, Rule::statement_block);
        //we want collect tokens from each case, but we need to preserve all variables
        //to consider: maybe instead of collecting tokens, we should return whole
        // deobfuscated if statement
        let results = self.results.clone();
        let current_variables = self.variables.clone();
        if let Err(err) = self.eval_statement_block(token.clone()) {
            log::debug!("Error during if_statement_collect_tokens: {:?}", err);
        }
        self.variables = current_variables;
        self.results = results;
    }

    fn eval_flow_control_statement(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        check_rule!(token, Rule::flow_control_statement);
        let token = token.into_inner().try_next()?;

        Ok(match token.as_rule() {
            Rule::flow_control_label_statement => Val::Null, //TODO
            Rule::flow_control_pipeline_statement => {
                let token = token.into_inner().try_next()?;
                //todo: throw, return or exit
                if let Some(pipeline_token) = token.into_inner().next() {
                    self.eval_pipeline(pipeline_token)?
                } else {
                    Val::Null
                }
            }
            _ => unexpected_token!(token),
        })
    }

    fn parse_switch_statement(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        check_rule!(token, Rule::switch_statement);
        let mut pairs = token.into_inner();

        let mut token = pairs.try_next()?;
        if Rule::switch_parameters == token.as_rule() {
            token = pairs.try_next()?;
        }

        let condition = match token.as_rule() {
            Rule::pipeline => self.eval_pipeline(token).ok(),
            Rule::switch_filename => None, //TODO: implement switch -file
            _ => None,
        };

        for case_token in pairs {
            check_rule!(case_token, Rule::switch_clause);
            let v = self.parse_switch_case(case_token, &condition)?;
            match v.len() {
                0 => {}
                1 => return Ok(v.into_iter().next().unwrap()),
                _ => return Ok(Val::Array(v)),
            }
        }

        Ok(Val::Null)
    }

    fn parse_switch_case(
        &mut self,
        token: Pair<'a>,
        switch_condition: &Option<Val>,
    ) -> ParserResult<Vec<Val>> {
        check_rule!(token, Rule::switch_clause);
        let mut pairs = token.into_inner();

        let switch_clause_condition = pairs.try_next()?;
        let clause_body = pairs.try_next()?;
        check_rule!(clause_body, Rule::statement_block);

        //first collect tokens
        self.statement_block_collect_tokens(clause_body.clone());

        //now we can evaluate the condition
        if switch_clause_condition
            .as_str()
            .eq_ignore_ascii_case("default")
            && let Ok(result) = self.safe_eval_statements(clause_body.clone())
        {
            return Ok(result);
        } else if switch_clause_condition.as_rule() == Rule::primary_expression
            && let Some(condition) = switch_condition
        {
            let val = self
                .eval_primary_expression(switch_clause_condition)
                .unwrap_or_default();

            if condition.eq(val, false).unwrap_or_default()
                && let Ok(result) = self.safe_eval_statements(clause_body)
            {
                return Ok(result);
            }
        }
        Ok(vec![])
    }

    fn parse_class_statement(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        check_rule!(token, Rule::class_statement);
        let mut pair = token.into_inner();

        let class_name_token = pair.try_next()?;
        check_rule!(class_name_token, Rule::simple_name);
        let class_name = class_name_token.as_str().to_string();

        let mut properties = ClassProperties::new();
        let mut methods: HashMap<String, ScriptBlock> = HashMap::new();

        for member_token in pair {
            match member_token.as_rule() {
                Rule::class_property_definition => {
                    let prop_pair = member_token.into_inner();

                    // we don't want care about attributes here. It's todo in future
                    let mut prop_pair = prop_pair.skip_while(|p| p.as_rule() == Rule::attribute);

                    let mut token = prop_pair.try_next()?;
                    let _is_static = if token.as_rule() == Rule::class_attribute_static {
                        token = prop_pair.try_next()?;
                        true
                    } else {
                        false
                    };

                    let _is_hidden = if token.as_rule() == Rule::class_attribute_hidden {
                        token = prop_pair.try_next()?;
                        true
                    } else {
                        false
                    };

                    let ttype = if token.as_rule() == Rule::type_literal {
                        let ttype = self.get_valtype_from_type_literal(token)?;
                        token = prop_pair.try_next()?;
                        Some(ttype)
                    } else {
                        None
                    };
                    check_rule!(token, Rule::variable);
                    let var_name = Self::parse_variable(token)?;
                    let default_val = if let Some(expression_token) = prop_pair.next() {
                        Some(self.eval_expression(expression_token)?)
                    } else {
                        None
                    };
                    properties.add_property(var_name.name, ttype, default_val);
                }
                Rule::class_method_definition => {
                    let prop_pair = member_token.into_inner();

                    // we don't want care about attributes here. It's todo in future
                    let mut prop_pair = prop_pair.skip_while(|p| p.as_rule() == Rule::attribute);

                    let mut token = prop_pair.try_next()?;
                    let _is_static = if token.as_rule() == Rule::class_attribute_static {
                        token = prop_pair.try_next()?;
                        true
                    } else {
                        false
                    };

                    let _is_hidden = if token.as_rule() == Rule::class_attribute_hidden {
                        token = prop_pair.try_next()?;
                        true
                    } else {
                        false
                    };

                    let _ttype = if token.as_rule() == Rule::type_literal {
                        let ttype = self.eval_type_literal(token)?.ttype();
                        token = prop_pair.try_next()?;
                        Some(ttype)
                    } else {
                        None
                    };
                    check_rule!(token, Rule::simple_name);
                    let method_name = token.as_str().to_ascii_lowercase();

                    let mut token = prop_pair.try_next()?;
                    let parameters = if token.as_rule() == Rule::parameter_list {
                        let params = self.parse_parameter_list(token)?;
                        token = prop_pair.try_next()?;
                        params
                    } else {
                        vec![]
                    };
                    check_rule!(token, Rule::script_block);

                    let method_name = MethodName::new(method_name.as_str(), &parameters);
                    let script_block = self.parse_script_block(token)?.with_params(parameters);
                    methods.insert(method_name.full_name().to_string(), script_block);
                }
                _ => unexpected_token!(member_token),
            }
        }
        let class_type = ClassType::new(class_name.clone(), properties, HashMap::new(), methods);
        self.types_map.insert(
            class_name.to_ascii_lowercase(),
            Box::new(class_type.clone()),
        );
        Ok(Val::Null)
    }

    fn eval_statement(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        match token.as_rule() {
            Rule::pipeline => self.eval_pipeline(token),
            Rule::if_statement => self.eval_if_statement(token),
            Rule::flow_control_statement => self.eval_flow_control_statement(token),
            Rule::function_statement => self.parse_function_statement(token),
            Rule::statement_terminator => Ok(Val::Null),
            Rule::class_statement => self.parse_class_statement(token),
            Rule::switch_statement => self.parse_switch_statement(token),
            Rule::EOI => Ok(Val::Null),
            _ => {
                not_implemented!(token)
            }
        }
    }

    fn safe_eval_sub_expr(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        check_rule!(token, Rule::sub_expression);
        let Some(inner_token) = token.into_inner().next() else {
            return Ok(Val::Null);
        };
        let mut inner_val = self.eval_pipeline(inner_token)?;
        if let Val::ScriptText(script) = &mut inner_val {
            *script = format!("$({})", script);
            //self.tokens.push(Token::SubExpression(script.clone()));
        }
        Ok(inner_val)
    }

    fn eval_statement_block(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        Ok(self
            .safe_eval_statements(token)?
            .iter()
            .last()
            .cloned()
            .unwrap_or(Val::Null))
    }

    fn eval_statements(&mut self, token: Pair<'a>) -> ParserResult<Vec<Val>> {
        //check_rule!(token, Rule::statements);
        let pairs = token.into_inner();
        let mut statements = vec![];

        for token in pairs {
            let s = self.eval_statement(token)?;
            statements.push(s);
        }
        Ok(statements)
    }

    fn safe_eval_statements(&mut self, token: Pair<'a>) -> ParserResult<Vec<Val>> {
        //check_rule!(token, Rule::statements);
        let pairs = token.into_inner();
        let mut statements = vec![];

        for token in pairs {
            match self.eval_statement(token.clone()) {
                Ok(s) => statements.push(s),
                Err(err) => {
                    self.errors.push(err);
                    statements.push(Val::ScriptText(token.as_str().to_string()));
                }
            }
        }
        Ok(statements)
    }

    fn parse_dq(&mut self, token: Pair<'a>) -> ParserResult<String> {
        let mut res_str = String::new();
        let pairs = token.into_inner();
        for token in pairs {
            let token = token.into_inner().try_next()?;
            let token_str = token.as_str();
            let res = match token.as_rule() {
                Rule::variable => self.get_variable(token).map(|v| v.cast_to_string()),
                Rule::sub_expression => self.safe_eval_sub_expr(token).map(|v| v.cast_to_string()),
                Rule::backtick_escape => Ok(token
                    .as_str()
                    .strip_prefix("`")
                    .unwrap_or_default()
                    .to_string()),
                _ => Ok(token.as_str().to_string()),
            };
            match res {
                Ok(s) => res_str.push_str(s.as_str()),
                Err(err) => {
                    self.errors.push(err);
                    res_str.push_str(token_str);
                }
            }
        }
        Ok(res_str)
    }

    fn eval_string_literal(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        check_rule!(token, Rule::string_literal);
        let mut pair = token.into_inner();
        let token = pair.try_next()?;
        let cloned_token = token.clone();

        let mut is_expandable = false;
        let res = match token.as_rule() {
            Rule::doublequoted_string_literal | Rule::doublequoted_multiline_string_literal => {
                is_expandable = true;
                self.parse_dq(token)?
            }
            Rule::singlequoted_string_literal => {
                let token_string = token.as_str().to_string();
                let stripped_prefix = token_string
                    .strip_prefix("'")
                    .unwrap_or(token_string.as_str());
                let stripped_suffix = stripped_prefix.strip_suffix("'").unwrap_or(stripped_prefix);
                stripped_suffix.to_string()
            }
            Rule::singlequoted_multiline_string_literal => {
                let mut res_str = String::new();
                let pairs = token.into_inner();
                for token in pairs {
                    res_str.push_str(token.as_str());
                }
                res_str
            }
            _ => unexpected_token!(token),
        };
        let ps_token = if is_expandable {
            Token::string_expandable(cloned_token.as_str().to_string(), res.clone())
        } else {
            Token::String(res.clone())
        };
        self.tokens.push(ps_token);

        Ok(Val::String(res.into()))
    }

    fn get_variable(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        check_rule!(token, Rule::variable);
        let var_name = Self::parse_variable(token)?;
        let Some(var) = self.variables.get(&var_name, &self.types_map) else {
            return Err(ParserError::VariableError(VariableError::NotDefined(
                var_name.name,
            )));
        };
        Ok(var)
    }

    fn parse_scoped_variable(token: Pair<'a>) -> ParserResult<VarName> {
        //can be scoped or splatted
        let mut pairs = token.into_inner();
        let mut token = pairs.try_next()?;

        let scope = if token.as_rule() == Rule::scope_keyword {
            let scope = token.as_str().to_ascii_lowercase();
            token = pairs.try_next()?;
            check_rule!(token, Rule::var_name);
            Some(Scope::from(scope.as_str()))
        } else {
            None
        };
        Ok(VarName::new(scope, token.as_str().to_ascii_lowercase()))
    }

    fn skip_value_access(&mut self, token: Pair<'a>) -> ParserResult<()> {
        check_rule!(token, Rule::value_access);
        let mut pair = token.into_inner();
        let token = pair.try_next()?;
        let mut val = self.eval_value(token)?;
        let _ = self.eval_element_access_ref(pair.next().unwrap(), &mut val)?;
        Err(ParserError::Skip)
    }

    // fn get_assignable_variable<'b>(&mut self, pairs: Pairs<'a>, object: &'b mut
    // Val) -> ParserResult<&'b mut Val> {     let mut var = object;
    //     for token in pairs {
    //             let tmp = self.variable_access(token, &mut var)?;
    //             var = tmp;
    //     }
    //     Ok(var)
    // }

    fn parse_assignable_variable(
        &mut self,
        token: Pair<'a>,
    ) -> ParserResult<(VarName, Option<Pairs<'a>>)> {
        check_rule!(token, Rule::assignable_variable);
        let mut pair = token.into_inner();
        let token = pair.try_next()?;
        match token.as_rule() {
            Rule::variable => {
                let var_name = Self::parse_variable(token)?;

                Ok((var_name, None))
            }
            Rule::variable_access => {
                let mut pairs = token.into_inner();
                let var_token = pairs.try_next()?;
                let var_name = Self::parse_variable(var_token)?;
                // let mut object = &mut var;
                // for token in pairs {
                //     object = self.variable_access(token, &mut object)?;
                // }
                Ok((var_name, Some(pairs)))
            }
            Rule::value_access => self
                .skip_value_access(token)
                .map(|()| (Default::default(), None)),
            _ => unexpected_token!(token),
        }
    }

    fn parse_variable(token: Pair<'a>) -> ParserResult<VarName> {
        check_rule!(token, Rule::variable);
        let mut pair = token.into_inner();
        let token = pair.try_next()?;

        Ok(match token.as_rule() {
            Rule::special_variable => {
                VarName::new_with_scope(Scope::Special, token.as_str().to_string())
            }
            Rule::parenthesized_variable => {
                Self::parse_variable(token.into_inner().next().unwrap())?
            }
            Rule::braced_variable => {
                let token = token.into_inner().try_next()?;
                let var = token.as_str().to_ascii_lowercase();
                let splits: Vec<&str> = var.split(":").collect();
                if splits.len() == 2 {
                    VarName::new_with_scope(Scope::from(splits[0]), splits[1].to_string())
                } else {
                    VarName::new(None, var)
                }
            }
            Rule::scoped_variable => Self::parse_scoped_variable(token)?,
            _ => unexpected_token!(token),
        })
    }

    fn eval_pre_arithmetic(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        check_rule!(token, Rule::pre_arithmetic);
        let mut pair = token.into_inner();
        let token = pair.try_next()?;

        let res = match token.as_rule() {
            Rule::pre_plus_expression => {
                let token = token.into_inner().try_next()?;
                match token.as_rule() {
                    Rule::variable => {
                        let var_name = Self::parse_variable(token)?;
                        self.variables
                            .get(&var_name, &self.types_map)
                            .unwrap_or_default()
                    }
                    Rule::parenthesized_expression => self.eval_parenthesized_expression(token)?,
                    _ => unexpected_token!(token),
                }
            }
            Rule::pre_minus_expression => {
                let token = token.into_inner().try_next()?;
                let mut v = Val::default();
                let to_sub = match token.as_rule() {
                    Rule::variable => {
                        let var_name = Self::parse_variable(token)?;
                        self.variables
                            .get(&var_name, &self.types_map)
                            .unwrap_or_default()
                    }
                    Rule::parenthesized_expression => self.eval_parenthesized_expression(token)?,
                    _ => unexpected_token!(token),
                };
                v.sub(to_sub)?;
                v
            }
            Rule::pre_inc_expression => {
                let variable_token = token.into_inner().try_next()?;
                let var_name = Self::parse_variable(variable_token)?;
                let mut var = self
                    .variables
                    .get(&var_name, &self.types_map)
                    .unwrap_or_default();
                var.inc()?;

                self.variables.set(&var_name, var.clone())?;
                var
            }
            Rule::pre_dec_expression => {
                let variable_token = token.into_inner().try_next()?;
                let var_name = Self::parse_variable(variable_token)?;
                let mut var = self
                    .variables
                    .get(&var_name, &self.types_map)
                    .unwrap_or_default();
                var.dec()?;

                self.variables.set(&var_name, var.clone())?;
                var
            }
            _ => unexpected_token!(token),
        };

        Ok(res)
    }

    fn eval_expression_with_unary_operator(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        check_rule!(token, Rule::expression_with_unary_operator);
        let mut pair = token.into_inner();
        let token = pair.try_next()?;

        let res = match token.as_rule() {
            Rule::pre_arithmetic => self.eval_pre_arithmetic(token)?,
            Rule::cast_expression => self.eval_cast_expression(token)?,
            Rule::negate_op => {
                let unary_token = pair.try_next()?;
                let unary = self.eval_unary_exp(unary_token)?;
                Val::Bool(!unary.cast_to_bool())
            }
            Rule::bitwise_negate_op => {
                let unary_token = pair.try_next()?;
                let unary = self.eval_unary_exp(unary_token)?;
                Val::Int(!unary.cast_to_int()?)
            }
            _ => unexpected_token!(token),
        };

        Ok(res)
    }

    fn eval_argument_list(&mut self, token: Pair<'a>) -> ParserResult<Vec<Val>> {
        check_rule!(token, Rule::argument_list);
        let mut pairs = token.into_inner();
        let token = pairs.try_next()?;

        self.skip_error += 1;

        let arg = self.eval_expression(token.clone())?;
        self.skip_error -= 1;

        if let Val::Array(vec) = arg {
            Ok(vec)
        } else {
            Ok(vec![arg])
        }
    }

    fn eval_member_access(&mut self, token: Pair<'a>) -> ParserResult<String> {
        //check_rule!(token, Rule::member_access);
        let member_name_token = token.into_inner().try_next()?;
        let member_name = member_name_token.as_str().to_ascii_lowercase();

        Ok(member_name)
    }

    fn method_is_static(&mut self, token: Pair<'a>) -> ParserResult<bool> {
        check_rule!(token, Rule::method_invocation);
        let mut pairs = token.into_inner();

        let access = pairs.try_next()?;
        match access.as_rule() {
            Rule::member_access => Ok(false),
            Rule::static_access => Ok(true),
            _ => unexpected_token!(access),
        }
    }

    fn eval_method_invocation(
        &mut self,
        token: Pair<'a>,
        object: &Val,
    ) -> ParserResult<(String, Vec<Val>)> {
        check_rule!(token, Rule::method_invocation);
        let token_string = token.as_str().to_string();

        let mut pairs = token.into_inner();

        let access = pairs.try_next()?;
        let method_name = self.eval_member_access(access)?;

        let args = if let Some(token) = pairs.next() {
            check_rule!(token, Rule::argument_list);
            match self.eval_argument_list(token) {
                Ok(args) => args,
                Err(e) => {
                    log::debug!("eval_argument_list error: {:?}", e);

                    //nevertheless push the function token
                    self.tokens.push(Token::method(
                        token_string.clone(),
                        object.clone().into(),
                        method_name.clone(),
                        Vec::new(),
                    ));
                    Err(e)?
                }
            }
        } else {
            Vec::new()
        };

        self.tokens.push(Token::method(
            token_string,
            object.clone().into(),
            method_name.clone(),
            args.clone().iter().map(|arg| arg.clone().into()).collect(),
        ));
        Ok((method_name, args))
    }

    fn eval_element_access_ref<'b>(
        &mut self,
        token: Pair<'a>,
        object: &'b mut Val,
    ) -> ParserResult<&'b mut Val> {
        let mut pairs = token.into_inner();
        let index_token = pairs.try_next()?;
        check_rule!(index_token, Rule::expression);
        let index = self.eval_expression(index_token)?;
        Ok(object.get_index_ref(index)?)
    }

    fn eval_element_access(&mut self, token: Pair<'a>, object: &Val) -> ParserResult<Val> {
        let mut pairs = token.into_inner();
        let index_token = pairs.try_next()?;
        check_rule!(index_token, Rule::expression);
        let index = self.eval_expression(index_token)?;
        Ok(object.get_index(index)?)
    }

    fn variable_access<'b>(
        &mut self,
        token: Pair<'a>,
        object: &'b mut Val,
    ) -> ParserResult<&'b mut Val> {
        fn get_member_name(token: Pair<'_>) -> &'_ str {
            token.into_inner().next().unwrap().as_str()
        }
        match token.as_rule() {
            Rule::static_access => {
                //todo
                Err(ParserError::NotImplemented(
                    "modificable static_member".into(),
                ))
            }
            Rule::member_access => Ok(object.member(get_member_name(token))?),
            Rule::element_access => Ok(self.eval_element_access_ref(token, object)?),
            _ => unexpected_token!(token),
        }
    }

    fn value_access(&mut self, token: Pair<'a>, object: &mut Val) -> ParserResult<Val> {
        fn get_member_name(token: Pair<'_>) -> &'_ str {
            token.into_inner().next().unwrap().as_str()
        }
        Ok(match token.as_rule() {
            Rule::static_access => {
                let Val::RuntimeType(rt) = object else {
                    return Err(RuntimeError::MethodNotFound(
                        "Readonly static members can only be accessed on types".to_string(),
                    )
                    .into());
                };
                rt.readonly_static_member(get_member_name(token))?
            }
            Rule::member_access => object.readonly_member(get_member_name(token))?.clone(),
            Rule::method_invocation => {
                let static_method = self.method_is_static(token.clone())?;
                let (function_name, args) = self.eval_method_invocation(token, object)?;
                let mangled_name = MethodName::from_args(function_name.as_str(), &args);

                if static_method {
                    let Val::RuntimeType(rt) = object else {
                        return Err(RuntimeError::MethodNotFound(
                            "Static method can be called only on type".to_string(),
                        )
                        .into());
                    };

                    let mut call = rt.static_method(mangled_name)?;
                    call(args)?
                } else {
                    let mut call = object.method(mangled_name)?;
                    call(object, args)?
                }
            }
            Rule::element_access => self.eval_element_access(token, object)?,
            _ => unexpected_token!(token),
        })
    }

    fn eval_value_access(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        check_rule!(token, Rule::value_access);
        let mut pairs = token.into_inner();
        let token = pairs.try_next()?;

        let mut object = self.eval_value(token)?;
        for token in pairs {
            object = self.value_access(token, &mut object)?;
        }
        log::debug!("Success eval_access: {:?}", object);
        Ok(object)
    }

    fn parse_access(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        check_rule!(token, Rule::value_access);
        let mut pairs = token.into_inner();
        let token = pairs.try_next()?;

        let mut object = self
            .eval_value(token.clone())
            .map(|v| v.cast_to_script())
            .unwrap_or(token.as_str().to_string());

        for token in pairs {
            match token.as_rule() {
                Rule::static_access => {
                    object.push_str(token.as_str());
                }
                Rule::member_access => {
                    object.push_str(token.as_str());
                }
                Rule::method_invocation => {
                    let static_method = self.method_is_static(token.clone())?;
                    let (method_name, args) = self
                        .eval_method_invocation(token.clone(), &Val::ScriptText(object.clone()))?;
                    log::trace!("Method: {:?} {:?}", &method_name, &args);

                    let separator = if static_method { "::" } else { "." };
                    object = format!(
                        "{}{separator}{}({})",
                        object,
                        method_name.to_ascii_lowercase(),
                        args.iter()
                            .map(|arg| arg.cast_to_script())
                            .collect::<Vec<String>>()
                            .join(", ")
                    )
                }
                Rule::element_access => {
                    let mut pairs = token.into_inner();
                    let index_token = pairs.try_next()?;
                    check_rule!(index_token, Rule::expression);
                    let index = self.eval_expression(index_token)?;
                    object = format!("{}[{}]", object, index);
                }
                _ => unexpected_token!(token),
            }
        }
        Ok(Val::String(object.into()))
    }

    fn eval_primary_expression(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        check_rule!(token, Rule::primary_expression);
        let mut pair = token.into_inner();
        let token = pair.try_next()?;
        let res = match token.as_rule() {
            Rule::value_access => match self.eval_value_access(token.clone()) {
                Ok(res) => res,
                Err(err) => {
                    log::debug!("eval_access error: {:?}", err);
                    self.errors.push(err);
                    self.parse_access(token)?
                }
            },
            Rule::value => self.eval_value(token)?,
            Rule::post_inc_expression => {
                let variable_token = token.into_inner().try_next()?;
                let var_name = Self::parse_variable(variable_token)?;
                let mut var = self
                    .variables
                    .get(&var_name, &self.types_map)
                    .unwrap_or_default();
                let var_to_return = var.clone();

                var.inc()?;
                self.variables.set(&var_name, var.clone())?;

                //if var_to_return.ttype() ==
                var_to_return
            }
            Rule::post_dec_expression => {
                let variable_token = token.into_inner().try_next()?;
                let var_name = Self::parse_variable(variable_token)?;
                let mut var = self
                    .variables
                    .get(&var_name, &self.types_map)
                    .unwrap_or_default();
                let var_to_return = var.clone();

                var.dec()?;
                self.variables.set(&var_name, var.clone())?;

                var_to_return
            }
            _ => unexpected_token!(token),
        };

        Ok(res)
    }

    fn get_valtype_from_type_literal(&mut self, token: Pair<'a>) -> ParserResult<ValType> {
        let type_literal = self.parse_type_literal(token)?;
        Ok(ValType::cast(type_literal.as_str(), &self.types_map)?)
    }

    fn eval_type_literal(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        let type_literal = self.parse_type_literal(token)?;
        match ValType::runtime_type_from_str(type_literal.as_str(), &self.types_map) {
            Ok(val_type) => Ok(val_type),
            Err(err) => {
                self.errors.push(err.into());
                Ok(Val::Null)
            }
        }
    }

    fn parse_type_literal(&mut self, token: Pair<'a>) -> ParserResult<String> {
        check_rule!(token, Rule::type_literal);

        let token = token.into_inner().try_next()?;
        check_rule!(token, Rule::type_spec);
        let type_literal = token.as_str().to_ascii_lowercase();
        self.tokens.push(Token::type_literal(type_literal.clone()));
        Ok(type_literal)
    }

    fn parse_script_block(&mut self, token: Pair<'a>) -> ParserResult<ScriptBlock> {
        check_rule!(token, Rule::script_block);

        let raw_text = token.as_str().to_string();

        let mut pairs = token.into_inner();
        let Some(token) = pairs.next() else {
            return Ok(ScriptBlock::new(vec![], String::new(), raw_text));
        };

        let params = self.parse_script_param_block(token.clone())?;
        //let params_str = token.as_str().to_string();

        let script_body = if let Some(token) = pairs.next() {
            check_rule!(token, Rule::script_block_body);
            token.as_str().to_string()
        } else {
            String::new()
        };
        //todo is it necessary?
        // Ok(if let Ok(deobfuscated_body) =
        // self.deobfuscate_script(&script_body) {
        //     ScriptBlock::new(params, deobfuscated_body.clone(),
        // format!("{};{}", params_str, deobfuscated_body)) } else {
        //     ScriptBlock::new(params, script_body, raw_text)
        // })
        self.script_block_collect_tokens(&script_body);

        Ok(ScriptBlock::new(params, script_body, raw_text))
    }

    pub(crate) fn script_block_collect_tokens(&mut self, script_body: &str) {
        //we want collect tokens from each case, but we need to preserve all variables
        //to consider: maybe instead of collecting tokens, we should return whole
        // deobfuscated if statement
        let results = self.results.clone();
        let current_variables = self.variables.clone();
        let errors = self.errors.clone();
        if let Err(err) = self.parse_subscript(script_body) {
            log::debug!("Error during script_block_collect_tokens: {:?}", err);
        }
        self.variables = current_variables;
        self.results = results;
        self.errors = errors;
    }

    fn parse_script_block_expression(&mut self, token: Pair<'a>) -> ParserResult<ScriptBlock> {
        check_rule!(token, Rule::script_block_expression);
        let mut pairs = token.into_inner();
        self.parse_script_block(pairs.next().unwrap())
    }

    fn eval_hash_key(&mut self, token: Pair<'a>) -> ParserResult<String> {
        check_rule!(token, Rule::key_expression);
        let mut pairs = token.into_inner();
        let key_token = pairs.try_next()?;

        Ok(match key_token.as_rule() {
            Rule::simple_name => key_token.as_str().to_ascii_lowercase(),
            Rule::unary_exp => self
                .eval_unary_exp(key_token)?
                .cast_to_string()
                .to_ascii_lowercase(),
            _ => unexpected_token!(key_token),
        })
    }

    fn eval_hash_entry(&mut self, token: Pair<'a>) -> ParserResult<(String, Val)> {
        check_rule!(token, Rule::hash_entry);

        let mut pairs = token.into_inner();
        let token_key = pairs.try_next()?;
        let token_value = pairs.try_next()?;
        let value = match token_value.as_rule() {
            //Rule::statement => self.eval_statement(token_value)?,
            Rule::type_literal => self.eval_type_literal(token_value)?,
            _ => self.eval_statement(token_value)?,
        };

        Ok((self.eval_hash_key(token_key)?, value))
    }

    fn eval_hash_literal(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        check_rule!(token, Rule::hash_literal_expression);
        let pairs = token.into_inner();
        let mut hash = HashMap::new();
        for token in pairs {
            let (key, value) = self.eval_hash_entry(token)?;
            hash.insert(key, value);
        }
        Ok(Val::HashTable(hash))
    }

    fn eval_parenthesized_expression(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        check_rule!(token, Rule::parenthesized_expression);
        let token = token.into_inner().try_next()?;
        self.safe_eval_pipeline(token)
    }

    fn eval_value(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        check_rule!(token, Rule::value);
        let mut pair = token.into_inner();
        let token = pair.try_next()?;

        let res = match token.as_rule() {
            Rule::parenthesized_expression => self.eval_parenthesized_expression(token)?,
            Rule::sub_expression | Rule::array_expression => {
                let statements = self.eval_statements(token)?;
                if statements.len() == 1 {
                    if let Val::Array(_) = statements[0] {
                        statements[0].clone()
                    } else {
                        Val::Array(statements)
                    }
                } else {
                    Val::Array(statements)
                }
            }
            Rule::script_block_expression => {
                Val::ScriptBlock(self.parse_script_block_expression(token)?)
            }
            Rule::hash_literal_expression => self.eval_hash_literal(token)?,
            Rule::string_literal => self.eval_string_literal(token)?,
            Rule::number_literal => self.eval_number_literal(token)?,
            Rule::type_literal => self.eval_type_literal(token)?,
            Rule::variable => self.get_variable(token)?,
            _ => unexpected_token!(token),
        };
        log::debug!("eval_value - res: {:?}", res);
        Ok(res)
    }

    fn eval_number_literal(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        check_rule!(token, Rule::number_literal);
        let mut negate = false;
        let mut pairs = token.into_inner();
        let mut token = pairs.try_next()?;

        //first handle prefix sign: + or -
        if token.as_rule() == Rule::minus {
            negate = true;
            token = pairs.try_next()?;
        } else if token.as_rule() == Rule::plus {
            token = pairs.try_next()?;
        }

        let mut val = self.eval_number(token)?;

        if negate {
            val.neg()?;
        }

        if let Some(unit) = pairs.next() {
            let unit = unit.as_str().to_ascii_lowercase();
            let unit_int = match unit.as_str() {
                "k" => 1024,
                "m" => 1024 * 1024,
                "g" => 1024 * 1024 * 1024,
                "t" => 1024 * 1024 * 1024 * 1024,
                "p" => 1024 * 1024 * 1024 * 1024 * 1024,
                _ => 1,
            };
            val.mul(Val::Int(unit_int))?;
        }
        Ok(val)
    }

    fn eval_number(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        check_rule!(token, Rule::number);
        let mut pairs = token.into_inner();
        let token = pairs.try_next()?;
        let v = match token.as_rule() {
            Rule::decimal_integer => {
                let int_val = token.into_inner().try_next()?;
                Val::Int(int_val.as_str().parse::<i64>().unwrap())
            }
            Rule::hex_integer => {
                let int_val = token.into_inner().try_next()?;
                Val::Int(i64::from_str_radix(int_val.as_str(), 16).unwrap())
            }
            Rule::float => {
                let float_str = token.as_str().trim();
                Val::Float(float_str.parse::<f64>()?)
                //todo: handle all border cases
            }
            _ => unexpected_token!(token),
        };
        Ok(v)
    }

    fn eval_unary_exp(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        check_rule!(token, Rule::unary_exp);
        let token = token.into_inner().try_next()?;
        match token.as_rule() {
            Rule::expression_with_unary_operator => self.eval_expression_with_unary_operator(token),
            Rule::primary_expression => self.eval_primary_expression(token),
            _ => unexpected_token!(token),
        }
    }

    fn eval_array_literal_exp_special_case(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        check_rule!(token, Rule::array_literal_exp_special_case);
        let mut pairs = token.into_inner();
        let token = pairs.try_next()?;

        let val = self.eval_array_literal_exp(token)?;
        Ok(Val::Array(vec![val]))
    }

    fn safe_parse_arg(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        Ok(if self.skip_error > 0 {
            match self.eval_unary_exp(token.clone()) {
                Ok(val) => val,
                Err(err) => {
                    self.errors.push(err);
                    Val::ScriptText(token.as_str().to_string())
                }
            }
        } else {
            self.eval_unary_exp(token.clone())?
        })
    }

    fn eval_array_literal_exp(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        check_rule!(token, Rule::array_literal_exp);
        let mut arr = Vec::new();
        let mut pairs = token.into_inner();
        let token = pairs.try_next()?;

        //if array literal starts with ',' we must eval single element as array too
        if let Rule::array_literal_exp_special_case = token.as_rule() {
            return self.eval_array_literal_exp_special_case(token);
        }

        arr.push(self.safe_parse_arg(token)?);
        for token in pairs {
            arr.push(self.safe_parse_arg(token)?);
        }

        Ok(if arr.len() == 1 {
            arr[0].clone()
        } else {
            Val::Array(arr)
        })
    }

    fn eval_range_exp(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        fn range(mut left: i64, right: i64) -> Vec<Val> {
            let mut v = Vec::new();
            if left <= right {
                loop {
                    v.push(left);
                    if left == right {
                        break;
                    }
                    left += 1;
                }
            } else {
                loop {
                    v.push(left);
                    if left == right {
                        break;
                    }
                    left -= 1;
                }
            }
            v.into_iter().map(Val::Int).collect()
        }
        check_rule!(token, Rule::range_exp);
        let mut pairs = token.into_inner();
        let mut token = pairs.try_next()?;

        let _is_minus = if let Rule::additive_op = token.as_rule() {
            token = pairs.try_next()?;
            true
        } else {
            false
        };

        let res = match token.as_rule() {
            Rule::decimal_integer => {
                let int_val = token.into_inner().try_next()?;
                let left = int_val.as_str().parse::<i64>().unwrap();
                let mut token = pairs.try_next()?;
                let _is_minus = if let Rule::additive_op = token.as_rule() {
                    token = pairs.try_next()?;
                    true
                } else {
                    false
                };
                let right = self.eval_array_literal_exp(token)?.cast_to_int()?;
                Val::Array(range(left, right))
            }
            Rule::array_literal_exp => {
                let res = self.eval_array_literal_exp(token)?;
                if let Some(token) = pairs.next() {
                    let left = res.cast_to_int()?;
                    let right = self.eval_array_literal_exp(token)?.cast_to_int()?;
                    Val::Array(range(left, right))
                } else {
                    res
                }
            }
            _ => unexpected_token!(token),
        };

        Ok(res)
    }

    fn eval_format_impl(
        &mut self,
        format: Vec<Val>,
        mut pairs: Pairs<'a>,
    ) -> ParserResult<Option<Vec<Val>>> {
        Ok(if let Some(token) = pairs.next() {
            let second_fmt = self.eval_range_exp(token)?;
            let format_arg =
                if let Some(vec_args) = self.eval_format_impl(second_fmt.cast_to_array(), pairs)? {
                    vec_args
                } else {
                    second_fmt.into_array()
                };

            Some(
                formatting::format_ps_vec(format, &format_arg)?
                    .iter()
                    .map(|v| Val::String(v.into()))
                    .collect(),
            )
        } else {
            None
        })
    }

    fn eval_format_exp(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        check_rule!(token, Rule::format_exp);
        let mut pairs = token.into_inner();
        let format = self.eval_range_exp(pairs.next().unwrap())?;
        if let Some(vec_args) = self.eval_format_impl(format.cast_to_array(), pairs)? {
            Ok(Val::String(
                vec_args
                    .iter()
                    .map(|v| v.cast_to_string())
                    .collect::<Vec<_>>()
                    .join(" ")
                    .into(),
            ))
        } else {
            Ok(format)
        }
    }

    fn eval_mult(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        check_rule!(token, Rule::multiplicative_exp);
        let mut pairs = token.into_inner();
        let mut res = self.eval_format_exp(pairs.next().unwrap())?;
        while let Some(op) = pairs.next() {
            let Some(fun) = ArithmeticPred::get(op.as_str()) else {
                log::error!("No arithmetic function for operator: {}", op.as_str());
                return Err(ParserError::NotImplemented(format!(
                    "No arithmetic function for operator: {}",
                    op.as_str()
                )));
            };

            let postfix = pairs.try_next()?;
            let right_op = self.eval_format_exp(postfix)?;
            res = fun(res, right_op)?;
        }

        Ok(res)
    }

    fn eval_additive(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        check_rule!(token, Rule::additive_exp);

        let mut pairs = token.into_inner();
        let token = pairs.try_next()?;
        let mut res = match token.as_rule() {
            Rule::multiplicative_exp => self.eval_mult(token)?,
            _ => unexpected_token!(token),
        };
        while let Some(op) = pairs.next() {
            //check_rule!(op, Rule::additive_op); plus or minus
            let Some(fun) = ArithmeticPred::get(op.as_str()) else {
                log::error!("No arithmetic function for operator: {}", op.as_str());
                return Err(ParserError::NotImplemented(format!(
                    "No arithmetic function for operator: {}",
                    op.as_str()
                )));
            };

            let mult = pairs.try_next()?;
            let right_op = match mult.as_rule() {
                Rule::multiplicative_exp => self.eval_mult(mult)?,
                Rule::pre_arithmetic => self.eval_pre_arithmetic(mult)?,
                _ => unexpected_token!(mult),
            };
            res = fun(res, right_op)?;
        }

        Ok(res)
    }

    fn eval_split_special_case(
        &mut self,
        script_block: ScriptBlock,
        input: Val,
    ) -> ParserResult<Vec<String>> {
        let mut res_vec = vec![];
        let mut parts = String::new();
        let input_str = input.cast_to_string();
        let characters = input_str.chars();

        // filtered_elements.join("")
        for ch in characters {
            let b = match script_block.run(vec![], self, Some(Val::String(ch.to_string().into()))) {
                Err(er) => {
                    self.errors.push(er);
                    false
                }
                Ok(res) => res.val.cast_to_bool(),
            };

            if b {
                res_vec.push(parts);
                parts = String::new();
            } else {
                parts.push(ch);
            }
        }
        self.variables.reset_ps_item();
        Ok(res_vec)
    }

    fn eval_comparison_exp(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        check_rule!(token, Rule::comparison_exp);
        let mut pairs = token.into_inner();
        let token = pairs.try_next()?;

        // we need to handle strange case. -split and -join can be invoke without
        // previous expression, eg. "-join 'some'"
        let mut res = if token.as_rule() == Rule::additive_exp {
            self.eval_additive(token)?
        } else {
            Val::Null
        };

        while let Some(op) = pairs.next() {
            let Some(fun) = StringPred::get(op.as_str()) else {
                log::error!("No string predicate for operator: {}", op.as_str());
                return Err(ParserError::NotImplemented(format!(
                    "No string predicate for operator: {}",
                    op.as_str()
                )));
            };

            let token = pairs.try_next()?;
            let right_op = match token.as_rule() {
                Rule::script_block_expression => {
                    let script_block = self.parse_script_block_expression(token)?;

                    return Ok(Val::Array(
                        self.eval_split_special_case(script_block, res)?
                            .into_iter()
                            .map(|s| Val::String(s.into()))
                            .collect::<Vec<_>>(),
                    ));
                }
                Rule::additive_exp => self.eval_additive(token)?,
                //Rule::type_literal => self.eval_type_literal(token)?,
                _ => unexpected_token!(token),
            };
            log::trace!("res: {:?}, right_op: {:?}", &res, &right_op);
            res = fun(res, right_op)?;
            log::trace!("res: {:?}", &res);
        }

        Ok(res)
    }

    fn parse_script_param_block(&mut self, token: Pair<'a>) -> ParserResult<Vec<Param>> {
        check_rule!(token, Rule::script_param_block);
        let mut pairs = token.into_inner();
        let Some(param_block_token) = pairs.next() else {
            return Ok(vec![]);
        };
        self.parse_param_block(param_block_token)
    }

    fn parse_param_block(&mut self, token: Pair<'a>) -> ParserResult<Vec<Param>> {
        check_rule!(token, Rule::param_block);
        let mut pairs = token.into_inner();

        let Some(token) = pairs.next() else {
            return Ok(vec![]);
        };

        let option_param_token = match token.as_rule() {
            Rule::attribute_list => {
                //self.parse_attribute_list(token)?;
                pairs.next()
            }
            Rule::parameter_list => Some(token),
            _ => unexpected_token!(token),
        };

        let Some(param_token) = option_param_token else {
            return Ok(vec![]);
        };
        self.parse_parameter_list(param_token)
    }

    fn parse_parameter_list(&mut self, token: Pair<'a>) -> ParserResult<Vec<Param>> {
        check_rule!(token, Rule::parameter_list);
        let mut params = vec![];
        let param_list_pairs = token.into_inner();
        for script_parameter_token in param_list_pairs {
            check_rule!(script_parameter_token, Rule::script_parameter);
            params.push(self.parse_script_parameter(script_parameter_token)?);
        }
        Ok(params)
    }

    fn parse_attribute_list(&mut self, token: Pair<'a>) -> ParserResult<Option<ValType>> {
        check_rule!(token, Rule::attribute_list);
        let attribute_list_pairs = token.into_inner();
        for attribute_token in attribute_list_pairs {
            check_rule!(attribute_token, Rule::attribute);
            let attribute_type_token = attribute_token.into_inner().try_next()?;
            match attribute_type_token.as_rule() {
                Rule::attribute_info => {
                    //skip for now
                    continue;
                }
                Rule::type_literal => {
                    return Ok(Some(
                        self.get_valtype_from_type_literal(attribute_type_token)?,
                    ));
                }
                _ => unexpected_token!(attribute_type_token),
            }
        }
        Ok(None)
    }
    fn parse_script_parameter(&mut self, token: Pair<'a>) -> ParserResult<Param> {
        check_rule!(token, Rule::script_parameter);
        let mut pairs = token.into_inner();
        let mut token = pairs.try_next()?;

        let type_literal = if token.as_rule() == Rule::attribute_list {
            let type_literal = self.parse_attribute_list(token).unwrap_or(None);
            token = pairs.try_next()?;
            type_literal
        } else {
            None
        };

        check_rule!(token, Rule::variable);
        let var_name = Self::parse_variable(token)?;

        let default_value = if let Some(default_value_token) = pairs.next() {
            check_rule!(default_value_token, Rule::script_parameter_default);
            let default_value_expr = default_value_token.into_inner().try_next()?;
            let default_value = self.eval_primary_expression(default_value_expr)?;
            Some(default_value)
        } else {
            None
        };
        Ok(Param::new(type_literal, var_name.name, default_value))
    }

    fn eval_bitwise_exp(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        check_rule!(token, Rule::bitwise_exp);

        let mut pairs = token.into_inner();
        let mut res = self.eval_as_exp(pairs.next().unwrap())?;
        while let Some(op) = pairs.next() {
            check_rule!(op, Rule::bitwise_operator);
            let Some(fun) = BitwisePred::get(op.as_str()) else {
                log::error!("No bitwise predicate for operator: {}", op.as_str());
                return Err(ParserError::NotImplemented(format!(
                    "No bitwise predicate for operator: {}",
                    op.as_str()
                )));
            };

            let mult = pairs.try_next()?;
            let right_op = self.eval_as_exp(mult)?;
            res = fun(res, right_op)?;
        }

        Ok(res)
    }

    fn eval_as_exp(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        check_rule!(token, Rule::as_expression);

        let mut pairs = token.into_inner();
        let mut res = self.eval_comparison_exp(pairs.next().unwrap())?;
        for token in pairs {
            let runtime_object = match token.as_rule() {
                Rule::primary_expression => self.eval_primary_expression(token)?,
                _ => unexpected_token!(token),
            };

            res = res.cast(&runtime_object).unwrap_or_default();
        }

        Ok(res)
    }

    fn parse_cmdlet_command_name(&mut self, token: Pair<'a>) -> ParserResult<Command> {
        check_rule!(token, Rule::cmdlet_command);

        let mut pairs = token.into_inner();
        let token = pairs.try_next()?;
        let command_name = match token.as_rule() {
            Rule::command_name => token.as_str(),
            Rule::where_command_name => "where-object",
            Rule::foreach_command_name => "foreach-object",
            Rule::powershell_command_name => "powershell",
            _ => unexpected_token!(token),
        };

        let mut command = Command::cmdlet(command_name);
        if Rule::command_name == token.as_rule() {
            command.set_session_scope(SessionScope::New);
        }
        Ok(command)
    }

    fn parse_command_args(&mut self, pairs: Pairs<'a>) -> ParserResult<Vec<CommandElem>> {
        let mut args = vec![];
        for command_element_token in pairs {
            let token_string = command_element_token.as_str().to_string();
            match command_element_token.as_rule() {
                Rule::command_argument => {
                    let arg_token = command_element_token.into_inner().try_next()?;
                    let arg = match arg_token.as_rule() {
                        Rule::array_literal_exp => self.eval_array_literal_exp(arg_token)?,
                        Rule::script_block_expression => {
                            Val::ScriptBlock(self.parse_script_block_expression(arg_token)?)
                        }
                        Rule::parenthesized_expression => {
                            self.eval_parenthesized_expression(arg_token)?
                        }
                        Rule::generic_token => {
                            let s = arg_token.as_str();
                            self.tokens.push(Token::String(s.into()));
                            Val::ScriptText(s.into())
                        }
                        _ => Val::ScriptText(arg_token.as_str().to_string()),
                    };
                    args.push(CommandElem::Argument(arg));
                }
                Rule::command_parameter => {
                    args.push(CommandElem::Parameter(token_string.to_ascii_lowercase()))
                }
                Rule::argument_list => {
                    let expression_token = command_element_token.into_inner().try_next()?;
                    let Ok(expr_res) = self.eval_expression(expression_token) else {
                        continue;
                    };
                    if let Val::Array(arr) = expr_res {
                        for v in arr {
                            args.push(CommandElem::Argument(v));
                        }
                    } else {
                        args.push(CommandElem::Argument(expr_res));
                    }
                }
                Rule::splatten_arg => {
                    let var_name = Self::parse_scoped_variable(command_element_token)?;
                    let var = self
                        .variables
                        .get(&var_name, &self.types_map)
                        .unwrap_or_default();
                    if let Val::HashTable(h) = var {
                        for (k, v) in h {
                            args.push(CommandElem::Parameter(format!("-{}", k)));
                            args.push(CommandElem::Argument(v));
                        }
                    }
                }
                Rule::redirection => { //todo: implement redirection
                }
                Rule::stop_parsing => { //todo: stop parsing
                }
                _ => unexpected_token!(command_element_token),
            }
        }
        Ok(args)
    }

    fn eval_command(&mut self, token: Pair<'a>, piped_arg: Option<Val>) -> ParserResult<Val> {
        check_rule!(token, Rule::command);
        let command_str = token.as_str().to_string();

        let mut pairs = token.into_inner();
        let command_token = pairs.try_next()?;
        let mut command = match command_token.as_rule() {
            Rule::cmdlet_command => self.parse_cmdlet_command_name(command_token)?,
            Rule::invocation_command => self.parse_invocation_command(command_token)?,
            _ => unexpected_token!(command_token),
        };

        let mut args = self.parse_command_args(pairs)?;
        if let Some(arg) = piped_arg {
            args.insert(0, CommandElem::Argument(arg));
        }

        command.with_args(args);
        self.tokens
            .push(Token::command(command_str, command.name(), command.args()));

        match command.execute(self) {
            Ok(CommandOutput {
                val,
                deobfuscated: _deobfuscated,
            }) => Ok(val),
            Err(e) => {
                self.errors.push(e);
                Ok(Val::ScriptText(command.to_string()))
            }
        }

        // if let Some(msg) = deobfuscated {
        //     self.add_deobfuscated_statement(msg);
        // }
    }

    fn add_deobfuscated_statement(&mut self, msg: String) {
        if let Some(last) = self.results.last_mut() {
            last.deobfuscated.push(msg);
        }
    }

    fn add_output_statement(&mut self, msg: StreamMessage) {
        if let Some(last) = self.results.last_mut() {
            last.output.push(msg);
        }
    }

    fn parse_invocation_command(&mut self, token: Pair<'a>) -> ParserResult<Command> {
        check_rule!(token, Rule::invocation_command);

        let invocation_command_token = token.into_inner().try_next()?;

        let mut session_scope = match invocation_command_token.as_rule() {
            Rule::current_scope_invocation_command => SessionScope::Current,
            Rule::new_scope_invocation_command => SessionScope::New,
            _ => unexpected_token!(invocation_command_token),
        };

        let token_inner = invocation_command_token.into_inner().try_next()?;

        let mut command = match token_inner.as_rule() {
            Rule::cmdlet_command => {
                session_scope = SessionScope::New;
                self.parse_cmdlet_command_name(token_inner)?
            }
            Rule::primary_expression => {
                let primary = self.eval_primary_expression(token_inner)?;
                if let Val::ScriptBlock(script_block) = primary {
                    Command::script_block(script_block)
                } else {
                    Command::cmdlet(&primary.cast_to_script())
                }
            }
            Rule::path_command_name => Command::path(token_inner.as_str()),
            _ => unexpected_token!(token_inner),
        };

        command.set_session_scope(session_scope);
        Ok(command)
    }

    fn eval_redirected_expression(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        check_rule!(token, Rule::redirected_expression);

        let expression_token = token.into_inner().try_next()?;
        //todo: handle redirections

        self.eval_expression(expression_token)
    }

    fn eval_expression(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        check_rule!(token, Rule::expression);
        let token_string = token.as_str().trim().to_string();

        let mut pairs = token.into_inner();
        let mut res = self.eval_bitwise_exp(pairs.next().unwrap())?;
        while let Some(op) = pairs.next() {
            check_rule!(op, Rule::logical_operator);
            let Some(fun) = LogicalPred::get(op.as_str()) else {
                log::error!("No logical predicate for operator: {}", op.as_str());
                return Err(ParserError::NotImplemented(format!(
                    "No logical predicate for operator: {}",
                    op.as_str()
                )));
            };

            let mult = pairs.try_next()?;
            let right_op = self.eval_bitwise_exp(mult)?;
            res = Val::Bool(fun(res, right_op));
        }
        self.tokens
            .push(Token::expression(token_string, res.clone().into()));

        if let Val::String(value::PsString(s)) = &res {
            self.tokens.push(Token::String(s.clone()));
        }

        Ok(res)
    }

    fn eval_pipeline_tail(&mut self, token: Pair<'a>, mut piped_arg: Val) -> ParserResult<Val> {
        check_rule!(token, Rule::pipeline_tail);
        let pairs = token.into_inner();

        for token in pairs {
            //self.variables.set_ps_item(arg);
            piped_arg = self.eval_command(token, Some(piped_arg))?;
        }

        Ok(piped_arg)
    }

    fn eval_pipeline_with_tail(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        check_rule!(token, Rule::pipeline_with_tail);
        let mut pairs = token.into_inner();
        let token = pairs.try_next()?;

        let result: Val = match token.as_rule() {
            Rule::redirected_expression => self.eval_redirected_expression(token)?,
            Rule::command => self.eval_command(token, None)?,
            _ => unexpected_token!(token),
        };

        if let Some(token) = pairs.next() {
            match token.as_rule() {
                Rule::pipeline_tail => Ok(self.eval_pipeline_tail(token, result)?),
                _ => unexpected_token!(token),
            }
        } else {
            Ok(result)
        }
    }

    fn eval_pipeline(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        check_rule!(token, Rule::pipeline);
        let mut pairs = token.into_inner();
        let token = pairs.try_next()?;

        match token.as_rule() {
            Rule::assignment_exp => self.eval_assigment_exp(token),
            Rule::pipeline_with_tail => self.eval_pipeline_with_tail(token),
            _ => unexpected_token!(token),
        }
    }

    fn safe_eval_pipeline(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        let res = self.eval_pipeline(token.clone());

        let v = match res {
            Ok(val) => val,
            Err(err) => {
                self.errors.push(err);
                Val::ScriptText(token.as_str().to_string())
            }
        };

        Ok(v)
    }

    fn eval_cast_expression(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        check_rule!(token, Rule::cast_expression);

        let mut pairs = token.into_inner();
        let type_token = pairs.try_next()?;
        check_rule!(type_token, Rule::type_literal);
        let val_type = self.eval_type_literal(type_token)?;
        let token = pairs.try_next()?;
        let res = match token.as_rule() {
            Rule::parenthesized_expression => self.eval_parenthesized_expression(token)?,
            Rule::unary_exp => self.eval_unary_exp(token)?,
            _ => unexpected_token!(token),
        };
        Ok(res.cast(&val_type)?)
    }

    fn eval_assigment_exp(&mut self, token: Pair<'a>) -> ParserResult<Val> {
        check_rule!(token, Rule::assignment_exp);

        let mut specified_type = None;

        let mut pairs = token.into_inner();
        let mut token = pairs.try_next()?;
        if token.as_rule() == Rule::type_literal {
            specified_type = Some(self.eval_type_literal(token)?);
            token = pairs.try_next()?;
        }
        let (var_name, access) = self.parse_assignable_variable(token)?;
        let mut variable = self
            .variables
            .get(&var_name, &self.types_map)
            .unwrap_or_default();
        let mut accessed_elem = &mut variable;

        // sometimes we have variable access like $a[0].Property, and we need access
        // property by reference
        if let Some(access) = access {
            for token in access {
                accessed_elem = self.variable_access(token, accessed_elem)?;
            }
        }
        let assignement_op = pairs.try_next()?;

        //get operand
        let op = assignement_op.into_inner().try_next()?;
        let pred = ArithmeticPred::get(op.as_str());

        let right_token = pairs.try_next()?;
        let right_op = self.eval_statement(right_token.clone())?;

        let Some(pred) = pred else {
            log::error!("No arithmetic function for operator: {}", op.as_str());
            return Err(ParserError::NotImplemented(format!(
                "No arithmetic function for operator: {}",
                op.as_str()
            )));
        };

        *accessed_elem = pred(accessed_elem.clone(), right_op)?;
        if let Some(runtime_type) = specified_type {
            *accessed_elem = accessed_elem.cast(&runtime_type)?;
        }
        self.variables.set(&var_name, variable.clone())?;
        //we want save each assignment statement
        self.add_deobfuscated_statement(format!("{} = {}", var_name, variable.cast_to_script()));

        Ok(Val::NonDisplayed(Box::new(variable)))
    }

    fn push_scope_session(&mut self) {
        self.variables.push_scope_session();
    }

    fn pop_scope_session(&mut self) {
        self.variables.pop_scope_session();
    }
}

#[cfg(test)]
mod tests {
    use pest::Parser;

    use super::*;

    #[test]
    fn comment_and_semicolon() {
        let input = r#"
# This is a single line comment
$a = 1; $b = 2; Write-Output $a

Write-Output "Hello"  # Another comment

<#
    This is a
    multi-line block comment
#>
"#;

        let _ = PowerShellSession::parse(Rule::program, input).unwrap();
    }

    #[test]
    fn while_loop() {
        let input = r#"
while ($true) {
    if ($someCondition) {
        break
    }
    # other code
}
"#;

        let _ = PowerShellSession::parse(Rule::program, input).unwrap();
    }

    #[test]
    fn foreach_loop() {
        let input = r#"
foreach ($n in $numbers) {
    Write-Output $n
}
"#;

        let _ = PowerShellSession::parse(Rule::program, input).unwrap();
    }

    #[test]
    fn for_loop() {
        let input = r#"
# Comma separated assignment expressions enclosed in parentheses.
for (($i = 0), ($j = 0); $i -lt 10; $i++)
{
    "`$i:$i"
    "`$j:$j"
}
"#;

        let _ = PowerShellSession::parse(Rule::program, input).unwrap();
    }

    #[test]
    fn switch() {
        let input = r#"
switch ($var) {
    "a" { Write-Output "A" }
    1 { Write-Output "One" }
    default { Write-Output "Other" }
}
"#;

        let _ = PowerShellSession::parse(Rule::program, input).unwrap();
    }

    #[test]
    fn functions() {
        let input = r#"
function Get-Square {
    param($x)
    return $x * $x
}

function Say-Hello {
    Write-Output "Hello"
}
"#;

        let _ = PowerShellSession::parse(Rule::program, input).unwrap();
    }

    #[test]
    fn if_expression() {
        let input = r#"
$x="hello"
        Write-Host $x
        $y = 42
        Start-Process "notepad.exe"

        $x = 42
if ($x -eq 1) {
    Write-Output "One"
} elseif ($x -eq 2) {
    Write-Output "Two"
} else {
    Write-Output "Other"
}
"#;

        let _ = PowerShellSession::parse(Rule::program, input).unwrap();
    }

    #[test]
    fn command() {
        let input = r#"
Get-Process | Where-Object { $_.CPU -gt 100 }
"#;

        let _ = PowerShellSession::parse(Rule::program, input).unwrap();
    }

    #[test]
    fn range() {
        let input = r#"
$numbers = 1..5
"#;

        let _ = PowerShellSession::parse(Rule::program, input).unwrap();
    }

    #[test]
    fn literals() {
        let input = r#"
$hex = 0xFF

$name = "Alice"
$msg = "Hello, $name. Today is $day."
$escaped = "She said: `"Hi`""
$literal = 'Hello, $name'
"#;

        let _ = PowerShellSession::parse(Rule::program, input).unwrap();
    }

    #[test]
    fn floats() {
        let input = r#"
    $pi = 3.1415
$half = .5
"#;

        let _ = PowerShellSession::parse(Rule::program, input).unwrap();
    }

    #[test]
    fn arrays() {
        let input = r#"
$a = 1, 2, 3
$b = @("one", "two", "three")
$c = @(1, 2, @(3, 4))
"#;

        let _ = PowerShellSession::parse(Rule::program, input).unwrap();
    }

    #[test]
    fn static_method_call() {
        let input = r#"
[Threading.Thread]::Sleep(399)
"#;

        let _ = PowerShellSession::parse(Rule::program, input).unwrap();
    }

    #[test]
    fn neg_pipeline() {
        let input = r#"
-not $input | Where-Object { $_ -gt 5 }
"#;

        let _ = PowerShellSession::parse(Rule::program, input).unwrap();
    }

    #[test]
    fn amsi_fail() {
        let input = r#"
#Matt Graebers second Reflection method 
$VMRviwsbtehQfPtxbt=$null;
$ilryNQSTt="System.$([cHAR]([ByTE]0x4d)+[ChAR]([byte]0x61)+[chAr](110)+[cHar]([byTE]0x61)+[cHaR](103)+[cHar](101*64/64)+[chaR]([byTE]0x6d)+[cHAr](101)+[CHAr]([byTE]0x6e)+[Char](116*103/103)).$([Char]([ByTe]0x41)+[Char](117+70-70)+[CHAr]([ByTE]0x74)+[CHar]([bYte]0x6f)+[CHar]([bytE]0x6d)+[ChaR]([ByTe]0x61)+[CHar]([bYte]0x74)+[CHAR]([byte]0x69)+[Char](111*26/26)+[chAr]([BYTe]0x6e)).$(('Âmsí'+'Ùtìl'+'s').NORmalizE([ChAR](44+26)+[chAR](111*9/9)+[cHar](82+32)+[ChaR](109*34/34)+[cHaR](68+24-24)) -replace [ChAr](92)+[CHaR]([BYTe]0x70)+[Char]([BytE]0x7b)+[CHaR]([BYTe]0x4d)+[chAR](110)+[ChAr](15+110))"

"#;

        let _ = PowerShellSession::parse(Rule::program, input).unwrap();
    }
}
