#![allow(
    dead_code,
    unused_imports,
    unused_variables,
    unused_assignments,
    unused
)]

pub mod cli;
pub mod completion;
pub mod detectors;
/// Provisioning project detector
/// Analyzes projects to detect technologies and infer infrastructure
/// requirements
///
/// # Architecture
///
/// ## Error Handling
/// Follows M-ERRORS-CANONICAL-STRUCTS pattern with situation-specific error
/// types
///
/// ## Detector Pattern
/// Each detector implements the `Detector` trait and detects one technology
/// Following the single-purpose principle from Nushell guidelines
///
/// ## Example
/// ```ignore
/// use provisioning_detector::{StackDetector, ProjectAnalysis};
/// use std::path::Path;
///
/// let analysis = StackDetector::new()
///     .detect_all(Path::new("/path/to/project"))
///     .expect("Failed to analyze project");
///
/// println!("Detected technologies: {:?}", analysis.detections);
/// println!("Inferred requirements: {:?}", analysis.requirements);
/// ```
pub mod error;
pub mod inference;
pub mod models;
pub mod questionnaire;

pub use cli::{Cli, Commands};
pub use completion::{ChangeTracker, Completer, GapAnalyzer, IncrementalMerger};
pub use detectors::Detector;
pub use error::{DetectionError, DetectionResult, InferenceError, InferenceResult};
pub use inference::InferenceEngine;
pub use models::{Detection, Evidence, ProjectAnalysis, Requirement, Technology};
pub use questionnaire::{
    Answer, DecisionNode, DecisionTree, Expression, InteractiveTUI, Question, QuestionKind,
    QuestionnaireEngine, QuestionnaireResponse, ValidationRule,
};

/// Main orchestrator for detecting all technologies in a project
pub struct StackDetector {
    detectors: Vec<Box<dyn detectors::Detector>>,
}

impl StackDetector {
    /// Create new StackDetector with default detectors
    pub fn new() -> Self {
        Self::with_default_detectors()
    }

    /// Create with default set of detectors
    pub fn with_default_detectors() -> Self {
        let detectors: Vec<Box<dyn detectors::Detector>> = vec![
            Box::new(detectors::NodeJsDetector::new()),
            Box::new(detectors::RustDetector::new()),
            Box::new(detectors::PythonDetector::new()),
            Box::new(detectors::DockerDetector::new()),
            Box::new(detectors::PostgresDetector::new()),
            Box::new(detectors::RedisDetector::new()),
        ];

        Self { detectors }
    }

    /// Run all detectors and return combined analysis
    pub fn detect_all(&self, project_path: &std::path::Path) -> DetectionResult<ProjectAnalysis> {
        let mut analysis = ProjectAnalysis::new(project_path.to_path_buf());

        // Sort detectors by priority (higher first)
        let mut sorted_detectors = self.detectors.iter().collect::<Vec<_>>();
        sorted_detectors.sort_by_key(|d| std::cmp::Reverse(d.priority()));

        // Run each detector
        for detector in sorted_detectors {
            if let Some(detection) = detector.detect(project_path)? {
                analysis.detections.push(detection);
            }
        }

        // Run inference engine
        let inference = InferenceEngine::with_default_rules();
        analysis.requirements = inference.infer_requirements(&analysis.detections);

        analysis.calculate_overall_confidence();

        Ok(analysis)
    }

    /// Add custom detector
    pub fn add_detector(mut self, detector: Box<dyn detectors::Detector>) -> Self {
        self.detectors.push(detector);
        self
    }
}

impl Default for StackDetector {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use tempfile::TempDir;

    use super::*;

    #[test]
    fn test_detect_nodejs_project() -> DetectionResult<()> {
        let temp_dir = TempDir::new().expect("Failed to create temp dir");
        let package_json_path = temp_dir.path().join("package.json");

        std::fs::write(
            &package_json_path,
            r#"{"name": "test", "dependencies": {"express": "^4.18.0"}}"#,
        )
        .expect("Failed to write package.json");

        let detector = StackDetector::new();
        let analysis = detector.detect_all(temp_dir.path())?;

        assert!(!analysis.detections.is_empty());
        assert!(analysis
            .detections
            .iter()
            .any(|d| d.technology == Technology::NodeJs));

        Ok(())
    }
}
