// src/validation/config.rs
//! Validation configuration types and builders

use crate::validation::{CharacterLimits, PatternFilters, DisplayMask};
#[cfg(feature = "validation")]
use crate::validation::{CustomFormatter, FormattingResult, PositionMapper};
use std::sync::Arc;

/// Main validation configuration for a field
#[derive(Clone, Default)]
pub struct ValidationConfig {
    /// Character limit configuration
    pub character_limits: Option<CharacterLimits>,

    /// Pattern filtering configuration
    pub pattern_filters: Option<PatternFilters>,

    /// User-defined display mask for visual formatting
    pub display_mask: Option<DisplayMask>,

    /// Optional: user-provided custom formatter (feature 4)
    #[cfg(feature = "validation")]
    pub custom_formatter: Option<Arc<dyn CustomFormatter + Send + Sync>>,

    /// Enable external validation indicator UI (feature 5)
    pub external_validation_enabled: bool,

    /// Future: External validation
    pub external_validation: Option<()>, // Placeholder for future implementation
}

/// Manual Debug to avoid requiring Debug on dyn CustomFormatter
impl std::fmt::Debug for ValidationConfig {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let mut ds = f.debug_struct("ValidationConfig");
        ds.field("character_limits", &self.character_limits)
          .field("pattern_filters", &self.pattern_filters)
          .field("display_mask", &self.display_mask)
          // Do not print the formatter itself to avoid requiring Debug
          .field(
              "custom_formatter",
              &{
                  #[cfg(feature = "validation")]
                  {
                      if self.custom_formatter.is_some() { &"Some(<CustomFormatter>)" } else { &"None" }
                  }
                  #[cfg(not(feature = "validation"))]
                  {
                      &"N/A"
                  }
              },
          )
          .field("external_validation_enabled", &self.external_validation_enabled)
          .field("external_validation", &self.external_validation)
          .finish()
    }
}

// ✅ FIXED: Move function from struct definition to impl block
impl ValidationConfig {
    /// If a custom formatter is configured, run it and return the formatted text,
    /// the position mapper and an optional warning message.
    ///
    /// Returns None when no custom formatter is configured.
    #[cfg(feature = "validation")]
    pub fn run_custom_formatter(
        &self,
        raw: &str,
    ) -> Option<(String, Arc<dyn PositionMapper>, Option<String>)> {
        let formatter = self.custom_formatter.as_ref()?;
        match formatter.format(raw) {
            FormattingResult::Success { formatted, mapper } => {
                Some((formatted, mapper, None))
            }
            FormattingResult::Warning { formatted, message, mapper } => {
                Some((formatted, mapper, Some(message)))
            }
            FormattingResult::Error { .. } => None, // Fall back to raw display
        }
    }

    /// Create a new empty validation configuration
    pub fn new() -> Self {
        Self::default()
    }

    /// Create a configuration with just character limits
    pub fn with_max_length(max_length: usize) -> Self {
        ValidationConfigBuilder::new()
            .with_max_length(max_length)
            .build()
    }

    /// Create a configuration with pattern filters
    pub fn with_patterns(patterns: PatternFilters) -> Self {
        ValidationConfigBuilder::new()
            .with_pattern_filters(patterns)
            .build()
    }

    /// Create a configuration with user-defined display mask
    ///
    /// # Examples
    /// ```
    /// use canvas::{ValidationConfig, DisplayMask};
    ///
    /// let phone_mask = DisplayMask::new("(###) ###-####", '#');
    /// let config = ValidationConfig::with_mask(phone_mask);
    /// ```
    pub fn with_mask(mask: DisplayMask) -> Self {
        ValidationConfigBuilder::new()
            .with_display_mask(mask)
            .build()
    }

    /// Validate a character insertion at a specific position (raw text space).
    ///
    /// Note: Display masks are visual-only and do not participate in validation.
    /// Editor logic is responsible for skipping mask separator positions; here we
    /// only validate the raw insertion against limits and patterns.
    pub fn validate_char_insertion(
        &self,
        current_text: &str,
        position: usize,
        character: char,
    ) -> ValidationResult {
        // Character limits validation
        if let Some(ref limits) = self.character_limits {
            // ✅ FIXED: Explicit return type annotation
            if let Some(result) = limits.validate_insertion(current_text, position, character) {
                if !result.is_acceptable() {
                    return result;
                }
            }
        }

        // Pattern filters validation
        if let Some(ref patterns) = self.pattern_filters {
            // ✅ FIXED: Explicit error handling
            if let Err(message) = patterns.validate_char_at_position(position, character) {
                return ValidationResult::error(message);
            }
        }

        // Future: Add other validation types here

        ValidationResult::Valid
    }

    /// Validate the current text content (raw text space)
    pub fn validate_content(&self, text: &str) -> ValidationResult {
        // Character limits validation
        if let Some(ref limits) = self.character_limits {
            // ✅ FIXED: Explicit return type annotation
            if let Some(result) = limits.validate_content(text) {
                if !result.is_acceptable() {
                    return result;
                }
            }
        }

        // Pattern filters validation
        if let Some(ref patterns) = self.pattern_filters {
            // ✅ FIXED: Explicit error handling
            if let Err(message) = patterns.validate_text(text) {
                return ValidationResult::error(message);
            }
        }

        // Future: Add other validation types here

        ValidationResult::Valid
    }

    /// Check if any validation rules are configured
    pub fn has_validation(&self) -> bool {
        self.character_limits.is_some()
            || self.pattern_filters.is_some()
            || self.display_mask.is_some()
            || {
                #[cfg(feature = "validation")]
                { self.custom_formatter.is_some() }
                #[cfg(not(feature = "validation"))]
                { false }
            }
    }

    pub fn allows_field_switch(&self, text: &str) -> bool {
        // Character limits validation
        if let Some(ref limits) = self.character_limits {
            // ✅ FIXED: Direct boolean return
            if !limits.allows_field_switch(text) {
                return false;
            }
        }

        // Future: Add other validation types here

        true
    }

    /// Get reason why field switching is blocked (if any)
    pub fn field_switch_block_reason(&self, text: &str) -> Option<String> {
        // Character limits validation
        if let Some(ref limits) = self.character_limits {
            // ✅ FIXED: Direct option return
            if let Some(reason) = limits.field_switch_block_reason(text) {
                return Some(reason);
            }
        }

        // Future: Add other validation types here

        None
    }
}

/// Builder for creating validation configurations
#[derive(Debug, Default)]
pub struct ValidationConfigBuilder {
    config: ValidationConfig,
}

impl ValidationConfigBuilder {
    /// Create a new validation config builder
    pub fn new() -> Self {
        Self::default()
    }

    /// Set character limits for the field
    pub fn with_character_limits(mut self, limits: CharacterLimits) -> Self {
        self.config.character_limits = Some(limits);
        self
    }

    /// Set pattern filters for the field
    pub fn with_pattern_filters(mut self, filters: PatternFilters) -> Self {
        self.config.pattern_filters = Some(filters);
        self
    }

    /// Set user-defined display mask for visual formatting
    ///
    /// # Examples
    /// ```
    /// use canvas::{ValidationConfigBuilder, DisplayMask};
    ///
    /// // Phone number with dynamic formatting
    /// let phone_mask = DisplayMask::new("(###) ###-####", '#');
    /// let config = ValidationConfigBuilder::new()
    ///     .with_display_mask(phone_mask)
    ///     .build();
    ///
    /// // Date with template formatting
    /// let date_mask = DisplayMask::new("##/##/####", '#')
    ///     .with_template('_');
    /// let config = ValidationConfigBuilder::new()
    ///     .with_display_mask(date_mask)
    ///     .build();
    ///
    /// // Custom business format
    /// let employee_id = DisplayMask::new("EMP-####-##", '#')
    ///     .with_template('•');
    /// let config = ValidationConfigBuilder::new()
    ///     .with_display_mask(employee_id)
    ///     .with_max_length(6)  // Only store the 6 digits
    ///     .build();
    /// ```
    pub fn with_display_mask(mut self, mask: DisplayMask) -> Self {
        self.config.display_mask = Some(mask);
        self
    }

    /// Set optional custom formatter (feature 4)
    #[cfg(feature = "validation")]
    pub fn with_custom_formatter<F>(mut self, formatter: Arc<F>) -> Self
    where
        F: CustomFormatter + Send + Sync + 'static,
    {
        self.config.custom_formatter = Some(formatter);
        // When custom formatter is present, it takes precedence over display mask.
        self.config.display_mask = None;
        self
    }

    /// Set maximum number of characters (convenience method)
    pub fn with_max_length(mut self, max_length: usize) -> Self {
        self.config.character_limits = Some(CharacterLimits::new(max_length));
        self
    }

    /// Enable or disable external validation indicator UI (feature 5)
    pub fn with_external_validation_enabled(mut self, enabled: bool) -> Self {
        self.config.external_validation_enabled = enabled;
        self
    }

    /// Build the final validation configuration
    pub fn build(self) -> ValidationConfig {
        self.config
    }
}

/// Result of a validation operation
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ValidationResult {
    /// Validation passed
    Valid,

    /// Validation failed with warning (input still accepted)
    Warning { message: String },

    /// Validation failed with error (input rejected)
    Error { message: String },
}

impl ValidationResult {
    /// Check if the validation result allows the input
    pub fn is_acceptable(&self) -> bool {
        matches!(self, ValidationResult::Valid | ValidationResult::Warning { .. })
    }

    /// Check if the validation result is an error
    pub fn is_error(&self) -> bool {
        matches!(self, ValidationResult::Error { .. })
    }

    /// Get the message if there is one
    pub fn message(&self) -> Option<&str> {
        match self {
            ValidationResult::Valid => None,
            ValidationResult::Warning { message } => Some(message),
            ValidationResult::Error { message } => Some(message),
        }
    }

    /// Create a warning result
    pub fn warning(message: impl Into<String>) -> Self {
        ValidationResult::Warning { message: message.into() }
    }

    /// Create an error result
    pub fn error(message: impl Into<String>) -> Self {
        ValidationResult::Error { message: message.into() }
    }
}

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

    #[test]
    fn test_config_with_user_defined_mask() {
        // User creates their own phone mask
        let phone_mask = DisplayMask::new("(###) ###-####", '#');
        let config = ValidationConfig::with_mask(phone_mask);

        // has_validation should be true because mask is configured
        assert!(config.has_validation());

        // Display mask is visual only; validation still focuses on raw content
        let result = config.validate_char_insertion("123", 3, '4');
        assert!(result.is_acceptable());

        // Content validation unaffected by mask
        let result = config.validate_content("1234567890");
        assert!(result.is_acceptable());
    }

    #[test]
    fn test_validation_config_builder() {
        let config = ValidationConfigBuilder::new()
            .with_max_length(10)
            .build();

        assert!(config.character_limits.is_some());
        assert_eq!(config.character_limits.unwrap().max_length(), Some(10));
    }

    #[test]
    fn test_config_builder_with_user_mask() {
        // User defines custom format
        let custom_mask = DisplayMask::new("##-##-##", '#').with_template('_');
        let config = ValidationConfigBuilder::new()
            .with_display_mask(custom_mask)
            .with_max_length(6)
            .build();

        assert!(config.has_validation());
        assert!(config.character_limits.is_some());
        assert!(config.display_mask.is_some());
    }

    #[test]
    fn test_validation_result() {
        let valid = ValidationResult::Valid;
        assert!(valid.is_acceptable());
        assert!(!valid.is_error());
        assert_eq!(valid.message(), None);

        let warning = ValidationResult::warning("Too long");
        assert!(warning.is_acceptable());
        assert!(!warning.is_error());
        assert_eq!(warning.message(), Some("Too long"));

        let error = ValidationResult::error("Invalid");
        assert!(!error.is_acceptable());
        assert!(error.is_error());
        assert_eq!(error.message(), Some("Invalid"));
    }

    #[test]
    fn test_config_with_max_length() {
        let config = ValidationConfig::with_max_length(5);
        assert!(config.has_validation());

        // Test valid insertion
        let result = config.validate_char_insertion("test", 4, 'x');
        assert!(result.is_acceptable());

        // Test invalid insertion (would exceed limit)
        let result = config.validate_char_insertion("tests", 5, 'x');
        assert!(!result.is_acceptable());
    }

    #[test]
    fn test_config_with_patterns() {
        use crate::validation::{PatternFilters, PositionFilter, PositionRange, CharacterFilter};

        let patterns = PatternFilters::new()
            .add_filter(PositionFilter::new(
                PositionRange::Range(0, 1),
                CharacterFilter::Alphabetic,
            ));

        let config = ValidationConfig::with_patterns(patterns);
        assert!(config.has_validation());

        // Test valid pattern insertion
        let result = config.validate_char_insertion("", 0, 'A');
        assert!(result.is_acceptable());

        // Test invalid pattern insertion
        let result = config.validate_char_insertion("", 0, '1');
        assert!(!result.is_acceptable());
    }
}
