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

use crate::context::input::InputContext;
use crate::context::StreamSolver;
use crate::evaluator::Evaluator;
use crate::expression::Expression;
use crate::input::Input;
use pel::expression::Expression as PelExpression;
use pel::runtime::value::Value as PelValue;
use pel::runtime::{Evaluation, Runtime, RuntimeError};
use thiserror::Error;

/// A builder for [`ScriptParser`]s.
pub struct ScriptingEngine {}

impl ScriptingEngine {
    /// Creates a new [`ScriptParser`].
    pub fn script(expression: &Expression) -> ScriptParser {
        ScriptParser::new(expression)
    }
}

/// Parser for script [`Expression`]s.
pub struct ScriptParser<'a> {
    expression: &'a Expression,
    input_context: InputContext,
}

impl<'a> ScriptParser<'a> {
    pub(crate) fn new(expression: &'a Expression) -> Self {
        Self {
            expression,
            input_context: InputContext::default(),
        }
    }

    /// Sets an `input`.
    pub fn input(mut self, input: Input) -> Self {
        match input {
            Input::Payload(fmt) => self.input_context.set_payload_input(fmt),
            Input::Vars(vars) => {
                self.input_context.push_var_input(vars);
            }
            Input::Attributes => {
                self.input_context.set_attributes_input();
            }
            Input::Authentication => self.input_context.set_authentication_input(),
        }
        self
    }

    /// Compiles the inputs and returns a new [`Script`].
    pub fn compile(self) -> Result<Script, ScriptError> {
        let result = Runtime::new()
            .eval_with_context(&self.expression.expression, &self.input_context)
            .map_err(ScriptError::Error)?;

        match result {
            Evaluation::Complete(_, v) => Ok(Script::new(None, None, Some(v), self.input_context)),
            Evaluation::Partial(e) => Ok(Script::new(
                self.expression.source.as_deref(),
                Some(e),
                None,
                self.input_context,
            )),
        }
    }
}

/// Represents the [`ScriptParser::compile`] error.
#[derive(Error, Debug)]
pub enum ScriptError {
    /// Represents a compiler error.
    #[error("Error compiling expression: {0}")]
    Error(RuntimeError),
}

/// An script relating an [`Expression`] with their [`Input`]s.
#[derive(Debug, Clone)]
pub struct Script {
    stream_solver: StreamSolver,
    evaluation: Option<PelExpression>,
    result: Option<PelValue>,
    context: InputContext,
}

impl Script {
    pub(crate) fn new(
        source: Option<&str>,
        evaluation: Option<PelExpression>,
        result: Option<PelValue>,
        context: InputContext,
    ) -> Self {
        Self {
            stream_solver: StreamSolver::for_source(source),
            evaluation,
            result,
            context,
        }
    }

    /// Creates an [`Evaluator`] for the script.
    pub fn evaluator(&self) -> Evaluator {
        Evaluator::new(
            self.stream_solver.clone(),
            self.evaluation.clone(),
            self.result.clone(),
            &self.context,
        )
    }
}