// src/validation/state.rs
//! Validation state management

use crate::validation::{ExternalValidationState, ValidationConfig, ValidationResult};
use std::collections::HashMap;

/// Validation state for all fields in a form
#[derive(Debug, Clone, Default)]
pub struct ValidationState {
    /// Validation configurations per field index
    field_configs: HashMap<usize, ValidationConfig>,

    /// Current validation results per field index
    field_results: HashMap<usize, ValidationResult>,

    /// Track which fields have been validated
    validated_fields: std::collections::HashSet<usize>,

    /// Global validation enabled/disabled
    enabled: bool,

    /// External validation results per field (Feature 5)
    external_results: HashMap<usize, ExternalValidationState>,

    last_switch_block: Option<String>,
}

impl ValidationState {
    /// Create a new validation state
    pub fn new() -> Self {
        Self {
            field_configs: HashMap::new(),
            field_results: HashMap::new(),
            validated_fields: std::collections::HashSet::new(),
            enabled: true,
            external_results: HashMap::new(),
            last_switch_block: None,
        }
    }

    /// Enable or disable validation globally
    pub fn set_enabled(&mut self, enabled: bool) {
        self.enabled = enabled;
        if !enabled {
            // Clear all validation results when disabled
            self.field_results.clear();
            self.validated_fields.clear();
            self.external_results.clear(); // Also clear external results
        }
    }

    /// Check if validation is enabled
    pub fn is_enabled(&self) -> bool {
        self.enabled
    }

    /// Set validation configuration for a field
    pub fn set_field_config(&mut self, field_index: usize, config: ValidationConfig) {
        if config.has_validation() || config.external_validation_enabled {
            self.field_configs.insert(field_index, config);
        } else {
            self.field_configs.remove(&field_index);
            self.field_results.remove(&field_index);
            self.validated_fields.remove(&field_index);
            self.external_results.remove(&field_index);
        }
    }

    /// Get validation configuration for a field
    pub fn get_field_config(&self, field_index: usize) -> Option<&ValidationConfig> {
        self.field_configs.get(&field_index)
    }

    /// Remove validation configuration for a field
    pub fn remove_field_config(&mut self, field_index: usize) {
        self.field_configs.remove(&field_index);
        self.field_results.remove(&field_index);
        self.validated_fields.remove(&field_index);
        self.external_results.remove(&field_index);
    }

    /// Set external validation state for a field (Feature 5)
    pub fn set_external_validation(&mut self, field_index: usize, state: ExternalValidationState) {
        self.external_results.insert(field_index, state);
    }

    /// Get current external validation state for a field
    pub fn get_external_validation(&self, field_index: usize) -> ExternalValidationState {
        self.external_results
            .get(&field_index)
            .cloned()
            .unwrap_or(ExternalValidationState::NotValidated)
    }

    /// Clear external validation state for a field
    pub fn clear_external_validation(&mut self, field_index: usize) {
        self.external_results.remove(&field_index);
    }

    /// Clear all external validation states
    pub fn clear_all_external_validation(&mut self) {
        self.external_results.clear();
    }

    /// Validate character insertion for a field
    pub fn validate_char_insertion(
        &mut self,
        field_index: usize,
        current_text: &str,
        position: usize,
        character: char,
    ) -> ValidationResult {
        if !self.enabled {
            return ValidationResult::Valid;
        }

        if let Some(config) = self.field_configs.get(&field_index) {
            let result = config.validate_char_insertion(current_text, position, character);

            // Store the validation result
            self.field_results.insert(field_index, result.clone());
            self.validated_fields.insert(field_index);

            result
        } else {
            ValidationResult::Valid
        }
    }

    /// Validate field content
    pub fn validate_field_content(&mut self, field_index: usize, text: &str) -> ValidationResult {
        if !self.enabled {
            return ValidationResult::Valid;
        }

        if let Some(config) = self.field_configs.get(&field_index) {
            let result = config.validate_content(text);

            // Store the validation result
            self.field_results.insert(field_index, result.clone());
            self.validated_fields.insert(field_index);

            result
        } else {
            ValidationResult::Valid
        }
    }

    /// Get current validation result for a field
    pub fn get_field_result(&self, field_index: usize) -> Option<&ValidationResult> {
        self.field_results.get(&field_index)
    }

    /// Get formatted display for a field if a custom formatter is configured.
    /// Returns (formatted_text, position_mapper, optional_warning_message).
    #[cfg(feature = "validation")]
    pub fn formatted_for(
        &self,
        field_index: usize,
        raw: &str,
    ) -> Option<(
        String,
        std::sync::Arc<dyn crate::validation::PositionMapper>,
        Option<String>,
    )> {
        let config = self.field_configs.get(&field_index)?;
        config.run_custom_formatter(raw)
    }

    /// Check if a field has been validated
    pub fn is_field_validated(&self, field_index: usize) -> bool {
        self.validated_fields.contains(&field_index)
    }

    /// Clear validation result for a field
    pub fn clear_field_result(&mut self, field_index: usize) {
        self.field_results.remove(&field_index);
        self.validated_fields.remove(&field_index);
    }

    /// Clear all validation results
    pub fn clear_all_results(&mut self) {
        self.field_results.clear();
        self.validated_fields.clear();
    }

    /// Get all field indices that have validation configured
    pub fn validated_field_indices(&self) -> impl Iterator<Item = usize> + '_ {
        self.field_configs.keys().copied()
    }

    /// Get all field indices with validation errors
    pub fn fields_with_errors(&self) -> impl Iterator<Item = usize> + '_ {
        self.field_results
            .iter()
            .filter(|(_, result)| result.is_error())
            .map(|(index, _)| *index)
    }

    /// Get all field indices with validation warnings
    pub fn fields_with_warnings(&self) -> impl Iterator<Item = usize> + '_ {
        self.field_results
            .iter()
            .filter(|(_, result)| matches!(result, ValidationResult::Warning { .. }))
            .map(|(index, _)| *index)
    }

    /// Check if any field has validation errors
    pub fn has_errors(&self) -> bool {
        self.field_results.values().any(|result| result.is_error())
    }

    /// Check if any field has validation warnings
    pub fn has_warnings(&self) -> bool {
        self.field_results
            .values()
            .any(|result| matches!(result, ValidationResult::Warning { .. }))
    }

    /// Get total count of fields with validation configured
    pub fn validated_field_count(&self) -> usize {
        self.field_configs.len()
    }

    /// Check if field switching is allowed for a specific field
    pub fn allows_field_switch(&self, field_index: usize, text: &str) -> bool {
        if !self.enabled {
            return true;
        }

        if let Some(config) = self.field_configs.get(&field_index) {
            config.allows_field_switch(text)
        } else {
            true // No validation configured, allow switching
        }
    }

    /// Get reason why field switching is blocked (if any)
    pub fn field_switch_block_reason(&self, field_index: usize, text: &str) -> Option<String> {
        if !self.enabled {
            return None;
        }

        if let Some(config) = self.field_configs.get(&field_index) {
            config.field_switch_block_reason(text)
        } else {
            None // No validation configured
        }
    }
    pub fn summary(&self) -> ValidationSummary {
        let total_validated = self.validated_fields.len();
        let errors = self.fields_with_errors().count();
        let warnings = self.fields_with_warnings().count();
        let valid = total_validated - errors - warnings;

        ValidationSummary {
            total_fields: self.field_configs.len(),
            validated_fields: total_validated,
            valid_fields: valid,
            warning_fields: warnings,
            error_fields: errors,
        }
    }

    /// Set the last switch block reason (for UI convenience)
    pub fn set_last_switch_block<S: Into<String>>(&mut self, reason: S) {
        self.last_switch_block = Some(reason.into());
    }

    /// Clear the last switch block reason
    pub fn clear_last_switch_block(&mut self) {
        self.last_switch_block = None;
    }

    /// Get the last switch block reason (if any)
    pub fn last_switch_block(&self) -> Option<&str> {
        self.last_switch_block.as_deref()
    }
}

/// Summary of validation state across all fields
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ValidationSummary {
    /// Total number of fields with validation configured
    pub total_fields: usize,

    /// Number of fields that have been validated
    pub validated_fields: usize,

    /// Number of fields with valid validation results
    pub valid_fields: usize,

    /// Number of fields with warnings
    pub warning_fields: usize,

    /// Number of fields with errors
    pub error_fields: usize,
}

impl ValidationSummary {
    /// Check if all configured fields are valid
    pub fn is_all_valid(&self) -> bool {
        self.error_fields == 0 && self.validated_fields == self.total_fields
    }

    /// Check if there are any errors
    pub fn has_errors(&self) -> bool {
        self.error_fields > 0
    }

    /// Check if there are any warnings
    pub fn has_warnings(&self) -> bool {
        self.warning_fields > 0
    }

    /// Get completion percentage (validated fields / total fields)
    pub fn completion_percentage(&self) -> f32 {
        if self.total_fields == 0 {
            1.0
        } else {
            self.validated_fields as f32 / self.total_fields as f32
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::validation::{CharacterLimits, ValidationConfigBuilder};

    #[test]
    fn test_validation_state_creation() {
        let state = ValidationState::new();
        assert!(state.is_enabled());
        assert_eq!(state.validated_field_count(), 0);
    }

    #[test]
    fn test_enable_disable() {
        let mut state = ValidationState::new();

        // Add some validation config
        let config = ValidationConfigBuilder::new().with_max_length(10).build();
        state.set_field_config(0, config);

        // Validate something
        let result = state.validate_field_content(0, "test");
        assert!(result.is_acceptable());
        assert!(state.is_field_validated(0));

        // Disable validation
        state.set_enabled(false);
        assert!(!state.is_enabled());
        assert!(!state.is_field_validated(0)); // Should be cleared

        // Validation should now return valid regardless
        let result = state.validate_field_content(0, "this is way too long for the limit");
        assert!(result.is_acceptable());
    }

    #[test]
    fn test_field_config_management() {
        let mut state = ValidationState::new();

        let config = ValidationConfigBuilder::new().with_max_length(5).build();

        // Set config
        state.set_field_config(0, config);
        assert_eq!(state.validated_field_count(), 1);
        assert!(state.get_field_config(0).is_some());

        // Remove config
        state.remove_field_config(0);
        assert_eq!(state.validated_field_count(), 0);
        assert!(state.get_field_config(0).is_none());
    }

    #[test]
    fn test_character_insertion_validation() {
        let mut state = ValidationState::new();

        let config = ValidationConfigBuilder::new().with_max_length(5).build();
        state.set_field_config(0, config);

        // Valid insertion
        let result = state.validate_char_insertion(0, "test", 4, 'x');
        assert!(result.is_acceptable());

        // Invalid insertion
        let result = state.validate_char_insertion(0, "tests", 5, 'x');
        assert!(!result.is_acceptable());

        // Check that result was stored
        assert!(state.is_field_validated(0));
        let stored_result = state.get_field_result(0);
        assert!(stored_result.is_some());
        assert!(!stored_result.unwrap().is_acceptable());
    }

    #[test]
    fn test_validation_summary() {
        let mut state = ValidationState::new();

        // Configure two fields
        let config1 = ValidationConfigBuilder::new().with_max_length(5).build();
        let config2 = ValidationConfigBuilder::new().with_max_length(10).build();
        state.set_field_config(0, config1);
        state.set_field_config(1, config2);

        // Validate field 0 (valid)
        state.validate_field_content(0, "test");

        // Validate field 1 (error)
        state.validate_field_content(1, "this is too long");

        let summary = state.summary();
        assert_eq!(summary.total_fields, 2);
        assert_eq!(summary.validated_fields, 2);
        assert_eq!(summary.valid_fields, 1);
        assert_eq!(summary.error_fields, 1);
        assert_eq!(summary.warning_fields, 0);

        assert!(!summary.is_all_valid());
        assert!(summary.has_errors());
        assert!(!summary.has_warnings());
        assert_eq!(summary.completion_percentage(), 1.0);
    }

    #[test]
    fn test_error_and_warning_tracking() {
        let mut state = ValidationState::new();

        let config = ValidationConfigBuilder::new()
            .with_character_limits(CharacterLimits::new_range(3, 10).with_warning_threshold(8))
            .build();
        state.set_field_config(0, config);

        // Too short (warning)
        state.validate_field_content(0, "hi");
        assert!(state.has_warnings());
        assert!(!state.has_errors());

        // Just right
        state.validate_field_content(0, "hello");
        assert!(!state.has_warnings());
        assert!(!state.has_errors());

        // Too long (error)
        state.validate_field_content(0, "hello world!");
        assert!(!state.has_warnings());
        assert!(state.has_errors());
    }
}
