//! Formula expression language for XBRL mappings.
//!
//! This module provides a formula expression language that allows users to define
//! calculated fields using arithmetic operations, field references, and functions.

use serde::{Deserialize, Serialize};

pub mod ast;
pub mod evaluator;
pub mod functions;
pub mod parser;
pub mod resolver;

#[allow(unused_imports)]
pub use evaluator::{
    evaluate_formula, evaluate_formula_with_resolver, FormulaContext, FormulaEvaluator,
};
pub use parser::FormulaParser;
pub use resolver::XbrlConceptResolver;

use crate::mappings::types::TargetField;

/// Error type for formula operations.
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum FormulaError {
    #[error("Syntax error at position {pos}: {message}")]
    Syntax { pos: usize, message: String },

    #[error("Unknown field: ${0}")]
    UnknownField(String),

    #[error("Unknown function: {0}")]
    UnknownFunction(String),

    #[error("Wrong number of arguments for {function}: expected {min}-{max}, got {actual}")]
    ArgCount {
        function: String,
        min: usize,
        max: usize,
        actual: usize,
    },

    #[error("Division by zero")]
    DivisionByZero,

    #[error("Modulo by zero")]
    ModuloByZero,

    #[error("Math error: {0}")]
    MathError(String),

    #[error("Circular reference detected: {0}")]
    CircularReference(String),

    #[error("Empty expression")]
    EmptyExpression,

    #[error("Evaluation error: {message}")]
    Evaluation { message: String },

    #[error("Concept not found in XBRL document: {taxonomy}:{concept}")]
    ConceptNotFound { taxonomy: String, concept: String },

    #[error("Concept {taxonomy}:{concept} not available in period {period}")]
    #[allow(dead_code)]
    ConceptNotAvailableInPeriod {
        taxonomy: String,
        concept: String,
        period: String,
    },

    #[error("Concept {taxonomy}:{concept} has no numeric value")]
    #[allow(dead_code)]
    ConceptNotNumeric { taxonomy: String, concept: String },

    #[error("Unit family mismatch: cannot combine {unit1} with {unit2}")]
    #[allow(dead_code)]
    UnitFamilyMismatch { unit1: String, unit2: String },
}

/// Result of formula validation.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FormulaValidationResult {
    /// Whether the formula is valid
    pub is_valid: bool,
    /// Error message if invalid
    #[serde(skip_serializing_if = "Option::is_none")]
    pub error: Option<String>,
    /// Field references found in the formula
    #[serde(skip_serializing_if = "Option::is_none")]
    pub field_refs: Option<Vec<TargetField>>,
    /// Functions used in the formula
    #[serde(skip_serializing_if = "Option::is_none")]
    pub functions: Option<Vec<String>>,
}

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

    #[test]
    fn test_formula_error_display() {
        let err = FormulaError::DivisionByZero;
        assert_eq!(err.to_string(), "Division by zero");

        let err = FormulaError::UnknownField("TestField".to_string());
        assert_eq!(err.to_string(), "Unknown field: $TestField");
    }
}
