pub mod auto_task;
pub mod basic;
pub mod core;
pub mod multimodal;
pub mod security;

pub mod analytics;
pub mod designer;
pub mod paper;
pub mod research;
pub mod sources;

pub use core::shared;

#[derive(Debug, Clone)]
pub enum BootstrapProgress {
    StartingBootstrap,
    InstallingComponent(String),
    StartingComponent(String),
    UploadingTemplates,
    ConnectingDatabase,
    StartingLLM,
    BootstrapComplete,
    BootstrapError(String),
}

pub use core::automation;
pub use core::bootstrap;
pub use core::bot;
pub use core::config;
pub use core::package_manager;
pub use core::session;

pub use security::{get_secure_port, SecurityConfig, SecurityManager};

#[cfg(feature = "attendance")]
pub mod attendance;

#[cfg(feature = "calendar")]
pub mod calendar;

#[cfg(feature = "compliance")]
pub mod compliance;

#[cfg(feature = "console")]
pub mod console;

#[cfg(feature = "directory")]
pub mod directory;

#[cfg(feature = "drive")]
pub mod drive;
#[cfg(feature = "drive")]
pub use drive::drive_monitor::DriveMonitor;

#[cfg(feature = "email")]
pub mod email;

#[cfg(feature = "instagram")]
pub mod instagram;

#[cfg(feature = "llm")]
pub mod llm;
#[cfg(feature = "llm")]
pub use llm::cache::{CacheConfig, CachedLLMProvider, CachedResponse, LocalEmbeddingService};
#[cfg(feature = "llm")]
pub use llm::DynamicLLMProvider;

#[cfg(feature = "meet")]
pub mod meet;

#[cfg(feature = "msteams")]
pub mod msteams;

#[cfg(feature = "nvidia")]
pub mod nvidia;

#[cfg(feature = "tasks")]
pub mod tasks;
#[cfg(feature = "tasks")]
pub use tasks::TaskEngine;

#[cfg(feature = "vectordb")]
#[path = "vector-db/mod.rs"]
pub mod vector_db;

#[cfg(feature = "weba")]
pub mod weba;

#[cfg(feature = "whatsapp")]
pub mod whatsapp;

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

    // Test configuration types from bottest/harness.rs

    #[derive(Debug, Clone)]
    pub struct TestConfig {
        pub postgres: bool,
        pub minio: bool,
        pub redis: bool,
        pub mock_zitadel: bool,
        pub mock_llm: bool,
        pub run_migrations: bool,
    }

    impl Default for TestConfig {
        fn default() -> Self {
            Self {
                postgres: true,
                minio: false,
                redis: false,
                mock_zitadel: true,
                mock_llm: true,
                run_migrations: true,
            }
        }
    }

    impl TestConfig {
        pub const fn minimal() -> Self {
            Self {
                postgres: false,
                minio: false,
                redis: false,
                mock_zitadel: false,
                mock_llm: false,
                run_migrations: false,
            }
        }

        pub const fn full() -> Self {
            Self {
                postgres: false,
                minio: false,
                redis: false,
                mock_zitadel: true,
                mock_llm: true,
                run_migrations: false,
            }
        }

        pub const fn database_only() -> Self {
            Self {
                postgres: true,
                minio: false,
                redis: false,
                mock_zitadel: false,
                mock_llm: false,
                run_migrations: true,
            }
        }
    }

    // Port allocation types from bottest/ports.rs

    #[derive(Debug)]
    pub struct TestPorts {
        pub postgres: u16,
        pub minio: u16,
        pub redis: u16,
        pub botserver: u16,
        pub mock_zitadel: u16,
        pub mock_llm: u16,
    }

    impl TestPorts {
        pub fn allocate_starting_from(base: u16) -> Self {
            Self {
                postgres: base,
                minio: base + 1,
                redis: base + 2,
                botserver: base + 3,
                mock_zitadel: base + 4,
                mock_llm: base + 5,
            }
        }
    }

    // Default ports from bottest/harness.rs

    pub struct DefaultPorts;

    impl DefaultPorts {
        pub const POSTGRES: u16 = 5432;
        pub const MINIO: u16 = 9000;
        pub const REDIS: u16 = 6379;
        pub const ZITADEL: u16 = 8080;
        pub const BOTSERVER: u16 = 4242;
    }

    // Expectation types from bottest/mocks/mod.rs

    #[derive(Debug, Clone)]
    pub struct Expectation {
        pub name: String,
        pub expected_calls: Option<usize>,
        pub actual_calls: usize,
        pub matched: bool,
    }

    impl Expectation {
        pub fn new(name: &str) -> Self {
            Self {
                name: name.to_string(),
                expected_calls: None,
                actual_calls: 0,
                matched: false,
            }
        }

        pub const fn times(mut self, n: usize) -> Self {
            self.expected_calls = Some(n);
            self
        }

        pub fn record_call(&mut self) {
            self.actual_calls += 1;
            self.matched = true;
        }

        pub fn verify(&self) -> Result<(), String> {
            if let Some(expected) = self.expected_calls {
                if self.actual_calls != expected {
                    return Err(format!(
                        "Expectation '{}' expected {} calls but got {}",
                        self.name, expected, self.actual_calls
                    ));
                }
            }
            Ok(())
        }
    }

    // Tests

    #[test]
    fn test_library_loads() {
        let version = env!("CARGO_PKG_VERSION");
        assert!(!version.is_empty());
    }

    #[test]
    fn test_config_default() {
        let config = TestConfig::default();
        assert!(config.postgres);
        assert!(!config.minio);
        assert!(!config.redis);
        assert!(config.mock_zitadel);
        assert!(config.mock_llm);
        assert!(config.run_migrations);
    }

    #[test]
    fn test_config_full() {
        let config = TestConfig::full();
        assert!(!config.postgres);
        assert!(!config.minio);
        assert!(!config.redis);
        assert!(config.mock_zitadel);
        assert!(config.mock_llm);
        assert!(!config.run_migrations);
    }

    #[test]
    fn test_config_minimal() {
        let config = TestConfig::minimal();
        assert!(!config.postgres);
        assert!(!config.minio);
        assert!(!config.redis);
        assert!(!config.mock_zitadel);
        assert!(!config.mock_llm);
        assert!(!config.run_migrations);
    }

    #[test]
    fn test_config_database_only() {
        let config = TestConfig::database_only();
        assert!(config.postgres);
        assert!(!config.minio);
        assert!(!config.redis);
        assert!(!config.mock_zitadel);
        assert!(!config.mock_llm);
        assert!(config.run_migrations);
    }

    #[test]
    fn test_port_allocation() {
        let ports = TestPorts::allocate_starting_from(15000);
        assert_eq!(ports.postgres, 15000);
        assert_eq!(ports.minio, 15001);
        assert_eq!(ports.redis, 15002);
        assert_eq!(ports.botserver, 15003);
        assert_eq!(ports.mock_zitadel, 15004);
        assert_eq!(ports.mock_llm, 15005);
    }

    #[test]
    fn test_default_ports() {
        assert_eq!(DefaultPorts::POSTGRES, 5432);
        assert_eq!(DefaultPorts::MINIO, 9000);
        assert_eq!(DefaultPorts::REDIS, 6379);
        assert_eq!(DefaultPorts::ZITADEL, 8080);
        assert_eq!(DefaultPorts::BOTSERVER, 4242);
    }

    #[test]
    fn test_expectation_basic() {
        let mut exp = Expectation::new("test");
        assert_eq!(exp.actual_calls, 0);
        assert!(!exp.matched);

        exp.record_call();
        assert_eq!(exp.actual_calls, 1);
        assert!(exp.matched);
    }

    #[test]
    fn test_expectation_times() {
        let mut exp = Expectation::new("test").times(2);
        exp.record_call();
        exp.record_call();

        assert!(exp.verify().is_ok());
    }

    #[test]
    fn test_expectation_times_fail() {
        let mut exp = Expectation::new("test").times(2);
        exp.record_call();

        assert!(exp.verify().is_err());
    }

    #[test]
    fn test_bootstrap_progress_variants() {
        let progress = BootstrapProgress::StartingBootstrap;
        assert!(matches!(progress, BootstrapProgress::StartingBootstrap));

        let progress = BootstrapProgress::InstallingComponent("postgres".to_string());
        assert!(matches!(
            progress,
            BootstrapProgress::InstallingComponent(_)
        ));

        let progress = BootstrapProgress::StartingComponent("redis".to_string());
        assert!(matches!(progress, BootstrapProgress::StartingComponent(_)));

        let progress = BootstrapProgress::BootstrapComplete;
        assert!(matches!(progress, BootstrapProgress::BootstrapComplete));

        let progress = BootstrapProgress::BootstrapError("test error".to_string());
        assert!(matches!(progress, BootstrapProgress::BootstrapError(_)));
    }

    #[test]
    fn test_ports_are_unique() {
        let ports = TestPorts::allocate_starting_from(20000);
        let all_ports = vec![
            ports.postgres,
            ports.minio,
            ports.redis,
            ports.botserver,
            ports.mock_zitadel,
            ports.mock_llm,
        ];

        let mut seen = std::collections::HashSet::new();
        for port in &all_ports {
            assert!(seen.insert(*port), "Duplicate port found: {}", port);
        }
    }

    #[test]
    fn test_expectation_multiple_calls() {
        let mut exp = Expectation::new("multi-call").times(5);

        for _ in 0..5 {
            exp.record_call();
        }

        assert_eq!(exp.actual_calls, 5);
        assert!(exp.verify().is_ok());
    }

    #[test]
    fn test_expectation_no_expected_calls() {
        let mut exp = Expectation::new("any-calls");

        exp.record_call();
        exp.record_call();
        exp.record_call();

        // Should pass verification since no expected call count was set
        assert!(exp.verify().is_ok());
    }
}
