#![allow(dead_code)]
#![allow(unused_imports)]
use anyhow::Result;
use std::sync::Arc;

use crate::auth::token_version_cache::TokenVersionCache;

/// Deployment environment. In `Production`, insecure config defaults are rejected
/// at boot so the service never starts with a known secret/encryption key.
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum Environment {
    Development,
    Production,
}

impl Environment {
    /// Parse `APP_ENVIRONMENT` (case-insensitive). Defaults to `Development`.
    pub fn from_env() -> Self {
        match std::env::var("APP_ENVIRONMENT")
            .unwrap_or_default()
            .to_ascii_lowercase()
            .as_str()
        {
            "prod" | "production" => Environment::Production,
            _ => Environment::Development,
        }
    }
}

/// Insecure sentinel values that must never be used in production.
const INSECURE_JWT_SECRET: &str = "secret";
const INSECURE_ENCRYPTION_KEY: &str = "default_key_32_bytes_long!";

#[derive(Clone)]
pub struct AppState {
    pub db: crate::db::MongoDb,
    pub jwt_service: crate::auth::JwtService,
    pub config: Config,
    pub audit_logger: Option<crate::security::AuditLogger>,
    pub session_manager: Option<crate::security::SessionManager>,
    pub account_lockout: Option<crate::security::AccountLockout>,
    pub health_stats_repo: Option<crate::models::health_stats::HealthStatisticsRepository>,
    pub mongo_client: Option<mongodb::Client>,

    /// Phase 2.8: Interaction checker service
    pub interaction_service: Option<Arc<crate::services::InteractionService>>,

    /// P0: short-TTL cache of {user_id -> token_version} so the JWT middleware
    /// can reject tokens whose version is stale (e.g. after a password change)
    /// without a Mongo lookup on every request.
    pub token_version_cache: Arc<TokenVersionCache>,

    /// P0: persisted (hashed) refresh tokens, used for rotation, revocation,
    /// and surviving process restarts.
    pub refresh_token_repo: Option<Arc<crate::models::refresh_token::RefreshTokenRepository>>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Config {
    pub server: ServerConfig,
    pub database: DatabaseConfig,
    pub jwt: JwtConfig,
    pub encryption: EncryptionConfig,
    pub cors: CorsConfig,
    pub environment: Environment,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ServerConfig {
    pub host: String,
    pub port: u16,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct DatabaseConfig {
    pub uri: String,
    pub database: String,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct JwtConfig {
    pub secret: String,
    pub access_token_expiry_minutes: i64,
    pub refresh_token_expiry_days: i64,
}

impl JwtConfig {
    pub fn access_token_expiry_duration(&self) -> std::time::Duration {
        std::time::Duration::from_secs(self.access_token_expiry_minutes as u64 * 60)
    }

    pub fn refresh_token_expiry_duration(&self) -> std::time::Duration {
        std::time::Duration::from_secs(self.refresh_token_expiry_days as u64 * 86400)
    }
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct EncryptionConfig {
    pub key: String,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct CorsConfig {
    pub allowed_origins: Vec<String>,
}

impl Config {
    pub fn from_env() -> Result<Self> {
        let environment = Environment::from_env();

        // JWT_SECRET — required to be a real secret in production.
        let jwt_secret =
            std::env::var("JWT_SECRET").unwrap_or_else(|_| INSECURE_JWT_SECRET.to_string());
        if environment == Environment::Production {
            if jwt_secret.is_empty() || jwt_secret == INSECURE_JWT_SECRET {
                anyhow::bail!(
                    "JWT_SECRET must be set to a strong, unique value in production (APP_ENVIRONMENT=production). \
                     Refusing to boot with an insecure default."
                );
            }
            tracing::info!("JWT_SECRET validated for production");
        } else if jwt_secret == INSECURE_JWT_SECRET {
            tracing::warn!(
                "JWT_SECRET is using the insecure default \"{}\". This is allowed only in development.",
                INSECURE_JWT_SECRET
            );
        }

        // ENCRYPTION_KEY — required to be a real key in production.
        let encryption_key =
            std::env::var("ENCRYPTION_KEY").unwrap_or_else(|_| INSECURE_ENCRYPTION_KEY.to_string());
        if environment == Environment::Production {
            if encryption_key.is_empty() || encryption_key == INSECURE_ENCRYPTION_KEY {
                anyhow::bail!(
                    "ENCRYPTION_KEY must be set to a strong, unique value in production (APP_ENVIRONMENT=production). \
                     Refusing to boot with an insecure default."
                );
            }
            tracing::info!("ENCRYPTION_KEY validated for production");
        } else if encryption_key == INSECURE_ENCRYPTION_KEY {
            tracing::warn!(
                "ENCRYPTION_KEY is using the insecure default. This is allowed only in development."
            );
        }

        Ok(Config {
            server: ServerConfig {
                host: std::env::var("NORMOGEN_HOST").unwrap_or_else(|_| "0.0.0.0".to_string()),
                port: std::env::var("NORMOGEN_PORT")
                    .unwrap_or_else(|_| "8080".to_string())
                    .parse()?,
            },
            database: DatabaseConfig {
                uri: std::env::var("MONGODB_URI")
                    .unwrap_or_else(|_| "mongodb://localhost:27017".to_string()),
                database: std::env::var("MONGODB_DATABASE")
                    .unwrap_or_else(|_| "normogen".to_string()),
            },
            jwt: JwtConfig {
                secret: jwt_secret,
                access_token_expiry_minutes: std::env::var("JWT_ACCESS_TOKEN_EXPIRY_MINUTES")
                    .unwrap_or_else(|_| "15".to_string())
                    .parse()?,
                refresh_token_expiry_days: std::env::var("JWT_REFRESH_TOKEN_EXPIRY_DAYS")
                    .unwrap_or_else(|_| "7".to_string())
                    .parse()?,
            },
            encryption: EncryptionConfig {
                key: encryption_key,
            },
            cors: CorsConfig {
                allowed_origins: std::env::var("CORS_ALLOWED_ORIGINS")
                    .unwrap_or_else(|_| "http://localhost:3000".to_string())
                    .split(',')
                    .map(|s| s.to_string())
                    .collect(),
            },
            environment,
        })
    }
}
