//! Numeric fields: `NumericField` (integer, decimal) with min/max validation and precision.
use crate::forms::base::*;
use crate::utils::trad::{t, tf};
use serde::Serialize;
use serde_json::json;
use std::sync::Arc;
use tera::{Context, Tera};

/// Numeric input (integer, decimal, float, percent, or range slider).
/// Construct with [`NumericField::integer`], [`::decimal`](NumericField::decimal),
/// [`::float`](NumericField::float), [`::percent`](NumericField::percent),
/// or [`::range`](NumericField::range).
#[derive(Clone, Serialize, Debug)]
pub struct NumericField {
    pub base: FieldConfig,
    pub config: NumericConfig,
    pub min_digits: Option<usize>,
    pub max_digits: Option<usize>,
}

impl CommonFieldConfig for NumericField {
    fn get_field_config(&self) -> &FieldConfig {
        &self.base
    }

    fn get_field_config_mut(&mut self) -> &mut FieldConfig {
        &mut self.base
    }
}

impl NumericField {
    fn create(name: &str, type_field: &str, config: NumericConfig) -> Self {
        Self {
            base: FieldConfig::new(name, type_field, "base_number"),
            config,
            min_digits: None,
            max_digits: None,
        }
    }
    /// Constrains the number of decimal digits (e.g. `digits(2, 4)` for `12.34` to `12.3456`).
    pub fn digits(mut self, min: usize, max: usize) -> Self {
        self.min_digits = Some(min);
        self.max_digits = Some(max);
        self
    }
    /// Integer input (`i64` range). No decimal part allowed.
    pub fn integer(name: &str) -> Self {
        Self::create(
            name,
            "number",
            NumericConfig::Integer {
                min: None,
                max: None,
            },
        )
    }

    /// Sets the HTML `placeholder` attribute.
    pub fn placeholder(mut self, p: &str) -> Self {
        self.set_placeholder(p);
        self
    }

    /// Floating-point input (`f64`). Accepts `,` as decimal separator.
    pub fn float(name: &str) -> Self {
        Self::create(name, "number", NumericConfig::Float { value: None })
    }

    /// Decimal input (arbitrary precision via `rust_decimal`). Accepts `,` as decimal separator.
    pub fn decimal(name: &str) -> Self {
        Self::create(name, "number", NumericConfig::Decimal { value: None })
    }

    /// Percentage input. Valid range: `0.0–100.0`.
    pub fn percent(name: &str) -> Self {
        Self::create(
            name,
            "number",
            NumericConfig::Percent {
                value: Range {
                    min: 0.0,
                    max: 100.0,
                },
            },
        )
    }

    /// Range slider `<input type="range">`. `default` is the initial value.
    pub fn range(name: &str, min: f64, max: f64, default: f64) -> Self {
        let mut field = Self::create(
            name,
            "range",
            NumericConfig::Range {
                value: Range { min, max },
                default,
                step: 1.0,
            },
        );
        field.base.value = default.to_string();
        field
    }

    /// Minimum accepted value. `msg` overrides the default error (pass `""` for default).
    pub fn min(mut self, val: f64, msg: &str) -> Self {
        match &mut self.config {
            NumericConfig::Integer { min, .. } => *min = Some(val as i64),
            NumericConfig::Float { value } | NumericConfig::Decimal { value, .. } => {
                if let Some(f) = value {
                    f.min = val;
                } else {
                    *value = Some(Range {
                        min: val,
                        max: f64::MAX,
                    });
                }
            }
            NumericConfig::Percent { value } | NumericConfig::Range { value, .. } => {
                value.min = val;
            }
        }
        if !msg.is_empty() {
            self.base
                .extra_context
                .insert("min_message".to_string(), json!(msg));
        }
        self
    }

    /// Maximum accepted value. `msg` overrides the default error (pass `""` for default).
    pub fn max(mut self, val: f64, msg: &str) -> Self {
        match &mut self.config {
            NumericConfig::Integer { max, .. } => *max = Some(val as i64),
            NumericConfig::Float { value } | NumericConfig::Decimal { value, .. } => {
                if let Some(f) = value {
                    f.max = val;
                } else {
                    *value = Some(Range {
                        min: f64::MIN,
                        max: val,
                    });
                }
            }
            NumericConfig::Percent { value } | NumericConfig::Range { value, .. } => {
                value.max = val;
            }
        }
        if !msg.is_empty() {
            self.base
                .extra_context
                .insert("max_message".to_string(), json!(msg));
        }
        self
    }

    /// Step increment for range sliders.
    pub fn step(mut self, s: f64) -> Self {
        if let NumericConfig::Range { step, .. } = &mut self.config {
            *step = s;
        }
        self
    }

    /// Overrides the auto-generated label.
    pub fn label(mut self, label: &str) -> Self {
        self.base.label = label.to_string();
        self
    }
}

// --- Trait Implementation ---
impl FormField for NumericField {
    fn validate(&mut self) -> bool {
        let val = self.base.value.trim();
        if self.base.is_required.choice && val.is_empty() {
            self.set_error(
                self.base
                    .is_required
                    .message
                    .clone()
                    .unwrap_or_else(|| t("forms.number_required").to_string()),
            );
            return false;
        }
        if val.is_empty() {
            return true;
        }

        let normalized = val.replace(',', ".");

        // --- STEP 1: Precision validation (digits) ---
        let current_digits = normalized
            .find('.')
            .map(|dot| normalized[dot.saturating_add(1)..].len())
            .unwrap_or(0);

        if current_digits < self.min_digits.unwrap_or(0) {
            self.set_error(tf("forms.precision_min", &[&self.min_digits.unwrap_or(0)]));
            return false;
        }
        if current_digits > self.max_digits.unwrap_or(usize::MAX) {
            self.set_error(tf(
                "forms.precision_max",
                &[&self.max_digits.unwrap_or(usize::MAX)],
            ));
            return false;
        }

        // --- STEP 2: Value bounds validation (min/max) ---
        match &self.config {
            NumericConfig::Integer { min, max } => {
                if let Ok(v) = normalized.parse::<i64>() {
                    if let Some(m) = min
                        && v < *m
                    {
                        self.set_error(tf("forms.min_value", &[m]));
                        return false;
                    }
                    if let Some(m) = max
                        && v > *m
                    {
                        self.set_error(tf("forms.max_value", &[m]));
                        return false;
                    }
                } else {
                    self.set_error(t("forms.integer_required").to_string());
                    return false;
                }
            }
            NumericConfig::Decimal { value, .. } | NumericConfig::Float { value } => {
                if let Ok(v) = normalized.parse::<f64>() {
                    if let Some(f) = value.as_ref() {
                        if v < f.min {
                            self.set_error(tf("forms.min_value", &[&f.min]));
                            return false;
                        }
                        if v > f.max {
                            self.set_error(tf("forms.max_value", &[&f.max]));
                            return false;
                        }
                    }
                } else {
                    self.set_error(t("forms.number_invalid").to_string());
                    return false;
                }
            }
            NumericConfig::Percent { value } | NumericConfig::Range { value, .. } => {
                match normalized.parse::<f64>() {
                    Ok(v) => {
                        if v < value.min || v > value.max {
                            self.set_error(t("forms.number_invalid").to_string());
                            return false;
                        }
                    }
                    Err(_) => {
                        self.set_error(t("forms.number_invalid").to_string());
                        return false;
                    }
                }
            }
        }
        self.clear_error();
        true
    }

    fn render(&self, tera: &Arc<Tera>) -> Result<String, String> {
        let mut context = Context::new();
        context.insert("field", &self.base);
        context.insert("config", &self.config);
        context.insert("readonly", &self.to_json_readonly());
        context.insert("disabled", &self.to_json_disabled());

        tera.render(&self.base.template_name, &context)
            .map_err(|e| {
                tf(
                    "forms.finalize_error",
                    &[&self.base.template_name, &e.to_string()],
                )
                .to_string()
            })
    }
}
