use std::collections::HashMap;
use std::fs;
use std::path::PathBuf;
use std::sync::Arc;

use axum::{
    extract::{Path, Query},
    http::StatusCode,
    response::Json,
    routing::get,
    Router,
};
use rustelo_core_lib::cache::content::ContentCache;
use rustelo_core_lib::cache::{current_timestamp, ContentCacheKey};
use rustelo_core_lib::ContentIndex;
use serde::{Deserialize, Serialize};
use tracing::{error, info, warn};

/// Get enabled content types from configuration
/// Uses the same source of truth as the rest of the system
fn get_enabled_content_types() -> Vec<String> {
    rustelo_core_lib::get_enabled_content_types()
        .iter()
        .map(|s| s.to_string())
        .collect()
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContentManifest {
    pub generated_at: String,
    pub languages: Vec<String>,
    pub content_types: Vec<String>,
    pub structure: ContentStructure,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContentStructure {
    /// Dynamic content types map - keys are content type names from
    /// content-kinds.toml
    #[serde(flatten)]
    pub content_types: HashMap<String, HashMap<String, ContentInfo>>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContentInfo {
    pub count: usize,
    pub available: bool,
}

#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct ContentLoader {
    pub content_base_path: PathBuf,
    pub supported_languages: Vec<String>,
    pub default_language: String,
    pub cache: Arc<ContentCache>,
}

#[derive(Debug, Deserialize)]
pub struct ContentQuery {
    #[allow(dead_code)]
    pub fallback: Option<bool>,
}

impl Default for ContentLoader {
    fn default() -> Self {
        Self {
            content_base_path: PathBuf::from("content"),
            supported_languages: vec!["en".to_string(), "es".to_string()],
            default_language: "en".to_string(),
            cache: Arc::new(ContentCache::new()),
        }
    }
}

impl ContentLoader {
    #[allow(dead_code)]
    pub fn new(content_base_path: PathBuf) -> Self {
        Self {
            content_base_path,
            cache: Arc::new(ContentCache::new()),
            ..Default::default()
        }
    }

    /// Create ContentLoader with a shared cache instance
    pub fn with_cache(content_base_path: PathBuf, cache: Arc<ContentCache>) -> Self {
        Self {
            content_base_path,
            cache,
            ..Default::default()
        }
    }

    #[allow(dead_code)]
    pub fn with_languages(mut self, languages: Vec<String>) -> Self {
        self.supported_languages = languages;
        self
    }

    #[allow(dead_code)]
    pub fn with_default_language(mut self, default_language: String) -> Self {
        self.default_language = default_language;
        self
    }

    pub async fn load_content_index(
        &self,
        content_type: &str,
        language: &str,
    ) -> Result<ContentIndex, ContentError> {
        // Use the unified shared loader that has integrated caching
        match rustelo_core_lib::fluent::loaders::load_content_index(content_type, language) {
            Ok(index) => {
                info!(
                    "✅ {} index for '{}' loaded via unified loader",
                    content_type, language
                );
                Ok(index)
            }
            Err(e) if language != self.default_language => {
                warn!(
                    "Failed to load {} index for language '{}', falling back to default: {}",
                    content_type, language, e
                );
                // Try with default language using unified loader
                match rustelo_core_lib::fluent::loaders::load_content_index(
                    content_type,
                    &self.default_language,
                ) {
                    Ok(index) => Ok(index),
                    Err(fallback_error) => Err(ContentError::Internal(format!(
                        "Failed to load content: {}",
                        fallback_error
                    ))),
                }
            }
            Err(e) => Err(ContentError::Internal(format!(
                "Failed to load content: {}",
                e
            ))),
        }
    }

    pub async fn load_content_item(
        &self,
        content_type: &str,
        language: &str,
        item_id: &str,
    ) -> Result<rustelo_core_lib::content::UnifiedContentItem, ContentError> {
        let index = self.load_content_index(content_type, language).await?;

        index
            .items
            .into_iter()
            .find(|item| item.id == item_id)
            .ok_or_else(|| ContentError::NotFound(format!("Content item '{}' not found", item_id)))
    }

    pub async fn load_content_manifest(&self) -> Result<ContentManifest, ContentError> {
        // Try to get from cache first
        let cache_key = ContentCacheKey::new("manifest", "main");

        if let Some(cached_value) = self.cache.get_content(&cache_key) {
            if let Ok(cached_manifest) =
                serde_json::from_value::<ContentManifest>((*cached_value).clone())
            {
                info!("Content manifest loaded from cache");
                return Ok(cached_manifest);
            }
        }

        let config = rustelo_core_lib::config::ContentManageConfig::from_env();
        let manifest_path = config.content_manifest_path();

        let manifest = if manifest_path.exists() {
            self.load_json_file(&manifest_path).await?
        } else {
            // Generate manifest on-the-fly
            self.generate_manifest().await?
        };

        // Cache the manifest
        let version = format!("v{}", current_timestamp());
        self.cache.cache_content(cache_key, &manifest, version);
        info!("Content manifest loaded and cached");

        Ok(manifest)
    }

    pub async fn generate_manifest(&self) -> Result<ContentManifest, ContentError> {
        // Get content types from content-kinds.toml configuration
        let content_types = get_enabled_content_types();

        let mut all_structures = HashMap::new();

        for content_type in &content_types {
            let mut type_structure = HashMap::new();

            for lang in &self.supported_languages {
                match self.load_content_index(content_type, lang).await {
                    Ok(index) => {
                        type_structure.insert(
                            lang.clone(),
                            ContentInfo {
                                count: index.items.len(),
                                available: true,
                            },
                        );
                    }
                    Err(_) => {
                        type_structure.insert(
                            lang.clone(),
                            ContentInfo {
                                count: 0,
                                available: false,
                            },
                        );
                    }
                }
            }

            all_structures.insert(content_type.to_string(), type_structure);
        }

        Ok(ContentManifest {
            generated_at: chrono::Utc::now().to_rfc3339(),
            languages: self.supported_languages.clone(),
            content_types,
            structure: ContentStructure {
                content_types: all_structures,
            },
        })
    }

    pub async fn get_available_translations(
        &self,
        content_type: &str,
        content_id: &str,
    ) -> Vec<String> {
        let mut available = Vec::new();

        for lang in &self.supported_languages {
            let exists = self
                .load_content_item(content_type, lang, content_id)
                .await
                .is_ok();

            if exists {
                available.push(lang.clone());
            }
        }

        available
    }

    async fn load_json_file<T>(&self, file_path: &PathBuf) -> Result<T, ContentError>
    where
        T: for<'de> Deserialize<'de>,
    {
        let content = fs::read_to_string(file_path).map_err(|e| {
            ContentError::FileNotFound(format!("Failed to read file {file_path:?}: {e}"))
        })?;

        serde_json::from_str(&content).map_err(|e| {
            ContentError::ParseError(format!("Failed to parse JSON from {file_path:?}: {e}"))
        })
    }

    pub fn is_language_supported(&self, language: &str) -> bool {
        self.supported_languages.contains(&language.to_string())
    }

    pub fn normalize_language(&self, language: &str) -> String {
        if self.is_language_supported(language) {
            language.to_string()
        } else {
            self.default_language.clone()
        }
    }

    /// Get cache statistics from unified cache
    pub fn get_cache_stats(&self) -> rustelo_core_lib::cache::content::ContentCacheStatistics {
        rustelo_core_lib::fluent::loaders::get_content_cache_stats()
    }

    /// Clear all caches via unified cache
    pub fn clear_cache(&self) {
        rustelo_core_lib::fluent::loaders::clear_content_cache();
        info!("✅ Content loader cache cleared via unified loader");
    }

    /// Clear cache for specific content type via unified cache
    pub fn invalidate_content_type(&self, content_type: &str) {
        rustelo_core_lib::fluent::loaders::invalidate_content_type_cache(content_type);
        info!(
            "✅ Cache invalidated for content type: {} via unified loader",
            content_type
        );
    }

    /// Cleanup expired cache entries via unified cache
    pub fn cleanup_expired_cache(&self) {
        rustelo_core_lib::fluent::loaders::cleanup_expired_content_cache();
    }
}

#[derive(Debug, Clone, thiserror::Error)]
pub enum ContentError {
    #[error("File not found: {0}")]
    FileNotFound(String),
    #[error("Parse error: {0}")]
    ParseError(String),
    #[error("Content not found: {0}")]
    NotFound(String),
    #[error("Language not supported: {0}")]
    UnsupportedLanguage(String),
    #[error("Internal error: {0}")]
    Internal(String),
}

impl From<ContentError> for StatusCode {
    fn from(error: ContentError) -> Self {
        match error {
            ContentError::FileNotFound(_) | ContentError::NotFound(_) => StatusCode::NOT_FOUND,
            ContentError::UnsupportedLanguage(_) => StatusCode::BAD_REQUEST,
            ContentError::ParseError(_) | ContentError::Internal(_) => {
                StatusCode::INTERNAL_SERVER_ERROR
            }
        }
    }
}

// HTTP handlers for content API
#[allow(dead_code)]
pub async fn get_content_index(
    Path((content_type, language)): Path<(String, String)>,
    Query(query): Query<ContentQuery>,
    content_loader: axum::extract::State<ContentLoader>,
) -> Result<Json<ContentIndex>, (StatusCode, String)> {
    // Get valid content types from content-kinds.toml configuration
    let valid_types = get_enabled_content_types();

    if !valid_types.contains(&content_type) {
        return Err((
            StatusCode::BAD_REQUEST,
            format!(
                "Invalid content type '{}'. Valid types: {}",
                content_type,
                valid_types.join(", ")
            ),
        ));
    }

    let lang = if content_loader.is_language_supported(&language) {
        language
    } else if query.fallback.unwrap_or(true) {
        content_loader.default_language.clone()
    } else {
        return Err((
            StatusCode::BAD_REQUEST,
            format!("Language '{language}' is not supported"),
        ));
    };

    match content_loader
        .load_content_index(&content_type, &lang)
        .await
    {
        Ok(index) => {
            info!("Loaded {} index for language: {}", content_type, lang);
            Ok(Json(index))
        }
        Err(e) => {
            error!(
                "Failed to load {} index for language '{}': {}",
                content_type, lang, e
            );
            Err((StatusCode::from(e.clone()), e.to_string()))
        }
    }
}

#[allow(dead_code)]
pub async fn get_content_item(
    Path((content_type, language, item_id)): Path<(String, String, String)>,
    Query(_query): Query<ContentQuery>,
    content_loader: axum::extract::State<ContentLoader>,
) -> Result<Json<rustelo_core_lib::content::UnifiedContentItem>, (StatusCode, String)> {
    // Get valid content types from content-kinds.toml configuration
    let valid_types = get_enabled_content_types();

    if !valid_types.contains(&content_type) {
        return Err((
            StatusCode::BAD_REQUEST,
            format!(
                "Invalid content type '{}'. Valid types: {}",
                content_type,
                valid_types.join(", ")
            ),
        ));
    }

    let lang = content_loader.normalize_language(&language);

    match content_loader
        .load_content_item(&content_type, &lang, &item_id)
        .await
    {
        Ok(item) => {
            info!(
                "Loaded {} item '{}' for language: {}",
                content_type, item_id, lang
            );
            Ok(Json(item))
        }
        Err(e) => {
            error!(
                "Failed to load {} item '{}' for language '{}': {}",
                content_type, item_id, lang, e
            );
            Err((StatusCode::from(e.clone()), e.to_string()))
        }
    }
}

#[allow(dead_code)]
pub async fn get_content_manifest(
    content_loader: axum::extract::State<ContentLoader>,
) -> Result<Json<ContentManifest>, (StatusCode, String)> {
    match content_loader.load_content_manifest().await {
        Ok(manifest) => {
            info!("Loaded content manifest");
            Ok(Json(manifest))
        }
        Err(e) => {
            error!("Failed to load content manifest: {}", e);
            Err((StatusCode::from(e.clone()), e.to_string()))
        }
    }
}

#[allow(dead_code)]
pub async fn get_available_translations(
    Path((content_type, content_id)): Path<(String, String)>,
    content_loader: axum::extract::State<ContentLoader>,
) -> Result<Json<Vec<String>>, (StatusCode, String)> {
    // Get valid content types from content-kinds.toml configuration
    let valid_types = get_enabled_content_types();

    if !valid_types.contains(&content_type) {
        return Err((
            StatusCode::BAD_REQUEST,
            format!(
                "Invalid content type '{}'. Valid types: {}",
                content_type,
                valid_types.join(", ")
            ),
        ));
    }

    let translations = content_loader
        .get_available_translations(&content_type, &content_id)
        .await;

    info!(
        "Found {} translations for {} '{}'",
        translations.len(),
        content_type,
        content_id
    );

    Ok(Json(translations))
}

/// Get cache statistics
#[allow(dead_code)]
pub async fn get_cache_stats(
    content_loader: axum::extract::State<ContentLoader>,
) -> Result<Json<rustelo_core_lib::cache::content::ContentCacheStatistics>, (StatusCode, String)> {
    let stats = content_loader.get_cache_stats();
    info!(
        "Cache statistics requested: {} total entries",
        stats.total_entries
    );
    Ok(Json(stats))
}

/// Clear all caches
#[allow(dead_code)]
pub async fn clear_cache(
    content_loader: axum::extract::State<ContentLoader>,
) -> Result<Json<serde_json::Value>, (StatusCode, String)> {
    content_loader.clear_cache();
    info!("All caches cleared via API");
    Ok(Json(
        serde_json::json!({"message": "All caches cleared successfully"}),
    ))
}

/// Invalidate cache for a specific content type
#[allow(dead_code)]
pub async fn invalidate_content_type(
    Path(content_type): Path<String>,
    content_loader: axum::extract::State<ContentLoader>,
) -> Result<Json<serde_json::Value>, (StatusCode, String)> {
    content_loader.invalidate_content_type(&content_type);
    info!("Cache invalidated for content type: {}", content_type);
    Ok(Json(serde_json::json!({
        "message": format!("Cache invalidated for content type: {}", content_type)
    })))
}

/// Cleanup expired cache entries
#[allow(dead_code)]
pub async fn cleanup_cache(
    content_loader: axum::extract::State<ContentLoader>,
) -> Result<Json<serde_json::Value>, (StatusCode, String)> {
    content_loader.cleanup_expired_cache();
    info!("Cache cleanup completed via API");
    Ok(Json(
        serde_json::json!({"message": "Cache cleanup completed successfully"}),
    ))
}

/// Create content loader routes (for static content loading API)
pub fn create_content_loader_routes() -> Router<ContentLoader> {
    Router::new()
        .route("/manifest", get(get_content_manifest))
        // Generic content routes (configuration-driven)
        .route("/{content_type}/{language}", get(get_content_index))
        .route(
            "/{content_type}/{language}/{item_id}",
            get(get_content_item),
        )
        .route(
            "/translations/{content_type}/{content_id}",
            get(get_available_translations),
        )
        // Cache management endpoints
        .route("/cache/stats", get(get_cache_stats))
        .route("/cache/clear", axum::routing::post(clear_cache))
        .route(
            "/cache/invalidate/{content_type}",
            axum::routing::post(invalidate_content_type),
        )
        .route("/cache/cleanup", axum::routing::post(cleanup_cache))
}

#[cfg(test)]
mod tests {
    use std::fs;

    use tempfile::TempDir;

    use super::*;

    fn create_test_content(temp_dir: &TempDir) -> ContentLoader {
        let content_path = temp_dir.path().join("content");

        // Create blog content
        let blog_en_dir = content_path.join("blog/en");
        fs::create_dir_all(&blog_en_dir).unwrap();

        let blog_index = r#"{
            "items": [
                {
                    "id": "test-post",
                    "title": "Test Post",
                    "subtitle": null,
                    "excerpt": "A test post",
                    "author": "Test Author",
                    "date": "2024-01-01",
                    "read_time": "5 min",
                    "tags": ["test"],
                    "featured": true,
                    "published": true,
                    "language": "en",
                    "source_file": "test.md",
                    "html_file": "test.html",
                    "translations": ["es"]
                }
            ],
            "pages": [],
            "language": "en"
        }"#;

        fs::write(blog_en_dir.join("index.json"), blog_index).unwrap();

        ContentLoader::new(content_path)
    }

    #[tokio::test]
    async fn test_load_content_index() {
        let temp_dir = TempDir::new().unwrap();
        let loader = create_test_content(&temp_dir);

        match loader.load_content_index("blog", "en").await {
            Ok(index) => {
                assert_eq!(index.language, "en");
                // The content loader uses the shared global loader, so it returns actual
                // project content
                assert!(!index.items.is_empty(), "Should load some blog content");
                // Verify that we can find valid blog post IDs
                assert!(
                    index.items.iter().any(|item| !item.id.is_empty()),
                    "Should have blog posts with valid IDs"
                );
            }
            Err(_) => {
                println!("Skipping test - project content files not available in test environment");
            }
        }
    }

    #[tokio::test]
    async fn test_load_nonexistent_language_fallback() {
        let temp_dir = TempDir::new().unwrap();
        let loader = create_test_content(&temp_dir);

        match loader.load_content_index("blog", "fr").await {
            Ok(index) => {
                assert_eq!(index.language, "en"); // Should fallback to default
            }
            Err(_) => {
                println!("Skipping test - project content files not available in test environment");
            }
        }
    }

    #[tokio::test]
    async fn test_generate_manifest() {
        let temp_dir = TempDir::new().unwrap();
        let loader = create_test_content(&temp_dir);

        let manifest = loader.generate_manifest().await.unwrap();
        assert_eq!(manifest.languages, vec!["en", "es"]);
        assert!(manifest
            .structure
            .content_types
            .get("blog")
            .unwrap()
            .contains_key("en"));
    }
}
