use anyhow::Result;
use shared::content::{ContentQuery, ContentState, ContentType, PageContent};
use std::collections::HashMap;
use std::path::Path;
use std::sync::Arc;
use uuid::Uuid;

use super::file_loader::FileContentLoader;
use super::repository::ContentRepository;

pub enum ContentSource {
    Database,
    #[allow(dead_code)]
    Files,
    #[allow(dead_code)]
    Both,
}

pub struct ContentService {
    repository: Arc<ContentRepository>,
    file_loader: Option<FileContentLoader>,
    content_source: ContentSource,
    cache: tokio::sync::RwLock<HashMap<String, PageContent>>,
    enable_cache: bool,
}

impl ContentService {
    pub fn new(repository: Arc<ContentRepository>) -> Self {
        Self {
            repository,
            file_loader: None,
            content_source: ContentSource::Database,
            cache: tokio::sync::RwLock::new(HashMap::new()),
            enable_cache: true,
        }
    }

    #[allow(dead_code)]
    pub fn with_file_loader<P: AsRef<Path>>(mut self, content_dir: P) -> Self {
        self.file_loader = Some(FileContentLoader::new(content_dir));
        self.content_source = ContentSource::Both;
        self
    }

    pub fn with_source(mut self, source: ContentSource) -> Self {
        self.content_source = source;
        self
    }

    pub fn with_cache(mut self, enable: bool) -> Self {
        self.enable_cache = enable;
        self
    }

    pub async fn get_content_by_slug(&self, slug: &str) -> Result<Option<PageContent>> {
        // Check cache first
        if self.enable_cache {
            let cache = self.cache.read().await;
            if let Some(content) = cache.get(slug) {
                return Ok(Some(content.clone()));
            }
        }

        let content = match &self.content_source {
            ContentSource::Database => self.repository.get_content_by_slug(slug).await?,
            ContentSource::Files => {
                if let Some(loader) = &self.file_loader {
                    loader.load_by_slug(slug)?
                } else {
                    None
                }
            }
            ContentSource::Both => {
                // Try database first, then files
                if let Some(content) = self.repository.get_content_by_slug(slug).await? {
                    Some(content)
                } else if let Some(loader) = &self.file_loader {
                    loader.load_by_slug(slug)?
                } else {
                    None
                }
            }
        };

        // Cache the result
        if self.enable_cache {
            if let Some(ref content) = content {
                let mut cache = self.cache.write().await;
                cache.insert(slug.to_string(), content.clone());
            }
        }

        Ok(content)
    }

    pub async fn get_content_by_id(&self, id: Uuid) -> Result<Option<PageContent>> {
        // For file-based content, we don't have IDs, so only check database
        match &self.content_source {
            ContentSource::Database | ContentSource::Both => {
                self.repository.get_content_by_id(&id).await
            }
            ContentSource::Files => Ok(None),
        }
    }

    pub async fn create_content(&self, content: &PageContent) -> Result<()> {
        match &self.content_source {
            ContentSource::Database | ContentSource::Both => {
                self.repository.create_content(content).await?;
            }
            ContentSource::Files => {
                return Err(anyhow::anyhow!("Cannot create content in file-only mode"));
            }
        }

        // Clear cache for the slug
        if self.enable_cache {
            let mut cache = self.cache.write().await;
            cache.remove(&content.slug);
        }

        Ok(())
    }

    #[allow(dead_code)]
    pub async fn update_content(&self, content: &PageContent) -> Result<()> {
        match &self.content_source {
            ContentSource::Database | ContentSource::Both => {
                self.repository.update_content(content).await?;
            }
            ContentSource::Files => {
                return Err(anyhow::anyhow!("Cannot update content in file-only mode"));
            }
        }

        // Clear cache for the slug
        if self.enable_cache {
            let mut cache = self.cache.write().await;
            cache.remove(&content.slug);
        }

        Ok(())
    }

    #[allow(dead_code)]
    pub async fn delete_content(&self, id: Uuid) -> Result<()> {
        match &self.content_source {
            ContentSource::Database | ContentSource::Both => {
                // Get the content first to clear cache
                if let Some(content) = self.repository.get_content_by_id(&id).await? {
                    self.repository.delete_content(&id).await?;

                    // Clear cache
                    if self.enable_cache {
                        let mut cache = self.cache.write().await;
                        cache.remove(&content.slug);
                    }
                }
            }
            ContentSource::Files => {
                return Err(anyhow::anyhow!("Cannot delete content in file-only mode"));
            }
        }

        Ok(())
    }

    pub async fn query_contents(&self, query: &ContentQuery) -> Result<Vec<PageContent>> {
        match &self.content_source {
            ContentSource::Database => {
                // TODO: Implement query_contents method
                Ok(vec![])
            }
            ContentSource::Files => {
                if let Some(loader) = &self.file_loader {
                    let mut contents = loader.load_all_content()?;

                    // Apply filters
                    if let Some(content_type) = &query.content_type {
                        contents.retain(|c| &c.content_type == content_type);
                    }

                    if let Some(state) = &query.state {
                        contents.retain(|c| &c.state == state);
                    }

                    if let Some(author_id) = &query.author_id {
                        contents.retain(|c| c.author_id == Some(*author_id));
                    }

                    if let Some(category) = &query.category {
                        contents.retain(|c| c.category.as_ref() == Some(category));
                    }

                    if let Some(tags) = &query.tags {
                        contents.retain(|c| tags.iter().any(|tag| c.tags.contains(tag)));
                    }

                    if let Some(require_login) = &query.require_login {
                        contents.retain(|c| c.require_login == *require_login);
                    }

                    if let Some(search) = &query.search {
                        let search_lower = search.to_lowercase();
                        contents.retain(|c| {
                            c.title.to_lowercase().contains(&search_lower)
                                || c.content.to_lowercase().contains(&search_lower)
                                || c.excerpt
                                    .as_ref()
                                    .map_or(false, |e| e.to_lowercase().contains(&search_lower))
                        });
                    }

                    // Apply sorting
                    contents.sort_by(|a, b| {
                        match query.sort_by.as_deref() {
                            Some("title") => a.title.cmp(&b.title),
                            Some("created_at") => a.created_at.cmp(&b.created_at),
                            Some("updated_at") => a.updated_at.cmp(&b.updated_at),
                            Some("published_at") => a.published_at.cmp(&b.published_at),
                            Some("view_count") => a.view_count.cmp(&b.view_count),
                            _ => b.created_at.cmp(&a.created_at), // Default: newest first
                        }
                    });

                    if query.sort_order.as_deref() == Some("DESC") {
                        contents.reverse();
                    }

                    // Apply pagination
                    let offset = query.offset.unwrap_or(0) as usize;
                    let limit = query.limit.map(|l| l as usize);

                    if offset > 0 {
                        contents = contents.into_iter().skip(offset).collect();
                    }

                    if let Some(limit) = limit {
                        contents.truncate(limit);
                    }

                    Ok(contents)
                } else {
                    Ok(Vec::new())
                }
            }
            ContentSource::Both => {
                // Combine results from both sources
                // TODO: Implement query_contents method
                let mut db_contents = vec![];

                if let Some(loader) = &self.file_loader {
                    let file_contents = loader.load_all_content()?;

                    // Filter file contents based on query
                    let filtered_file_contents: Vec<_> = file_contents
                        .into_iter()
                        .filter(|c| {
                            if let Some(content_type) = &query.content_type {
                                if &c.content_type != content_type {
                                    return false;
                                }
                            }
                            if let Some(state) = &query.state {
                                if &c.state != state {
                                    return false;
                                }
                            }
                            true
                        })
                        .collect();

                    // Merge contents, avoiding duplicates by slug
                    let mut slugs: std::collections::HashSet<String> = db_contents
                        .iter()
                        .map(|c: &PageContent| c.slug.clone())
                        .collect();

                    for content in filtered_file_contents {
                        if !slugs.contains(&content.slug) {
                            slugs.insert(content.slug.clone());
                            db_contents.push(content);
                        }
                    }
                }

                Ok(db_contents)
            }
        }
    }

    pub async fn get_published_contents(&self, limit: Option<i64>) -> Result<Vec<PageContent>> {
        let query = ContentQuery::new()
            .with_state(ContentState::Published)
            .with_pagination(limit.unwrap_or(50), 0);

        self.query_contents(&query).await
    }

    pub async fn get_contents_by_type(
        &self,
        content_type: ContentType,
    ) -> Result<Vec<PageContent>> {
        let query = ContentQuery::new()
            .with_content_type(content_type)
            .with_state(ContentState::Published);

        self.query_contents(&query).await
    }

    pub async fn get_contents_by_author(&self, author_id: Uuid) -> Result<Vec<PageContent>> {
        let query = ContentQuery::new()
            .with_author(author_id)
            .with_state(ContentState::Published);

        self.query_contents(&query).await
    }

    pub async fn get_contents_by_category(&self, category: &str) -> Result<Vec<PageContent>> {
        let query = ContentQuery::new()
            .with_category(category.to_string())
            .with_state(ContentState::Published);

        self.query_contents(&query).await
    }

    #[allow(dead_code)]
    pub async fn get_contents_by_tags(&self, tags: Vec<String>) -> Result<Vec<PageContent>> {
        let query = ContentQuery::new()
            .with_tags(tags)
            .with_state(ContentState::Published);

        self.query_contents(&query).await
    }

    pub async fn search_contents(
        &self,
        search_term: &str,
        limit: Option<i64>,
    ) -> Result<Vec<PageContent>> {
        let query = ContentQuery::new()
            .with_search(search_term.to_string())
            .with_state(ContentState::Published)
            .with_pagination(limit.unwrap_or(20), 0);

        self.query_contents(&query).await
    }

    pub async fn increment_view_count(&self, id: Uuid) -> Result<()> {
        match &self.content_source {
            ContentSource::Database | ContentSource::Both => {
                // TODO: Implement increment_view_count method
                // self.repository.increment_view_count(&id).await?;
            }
            ContentSource::Files => {
                // For file-based content, we can't increment view count
                tracing::warn!("Cannot increment view count for file-based content");
            }
        }

        Ok(())
    }

    pub async fn get_content_stats(&self) -> Result<ContentStats> {
        match &self.content_source {
            ContentSource::Database | ContentSource::Both => {
                // TODO: Implement get_content_count method
                let total_count = 0;
                // TODO: Implement get_published_count method
                let published_count = 0;
                // TODO: Implement get_draft_count method
                let draft_count = 0;

                Ok(ContentStats {
                    total_count,
                    published_count,
                    draft_count,
                })
            }
            ContentSource::Files => {
                if let Some(loader) = &self.file_loader {
                    let contents = loader.load_all_content()?;
                    let total_count = contents.len() as i64;
                    let published_count =
                        contents.iter().filter(|c| c.is_published()).count() as i64;
                    let draft_count = contents
                        .iter()
                        .filter(|c| matches!(c.state, ContentState::Draft))
                        .count() as i64;

                    Ok(ContentStats {
                        total_count,
                        published_count,
                        draft_count,
                    })
                } else {
                    Ok(ContentStats {
                        total_count: 0,
                        published_count: 0,
                        draft_count: 0,
                    })
                }
            }
        }
    }

    pub async fn get_recent_contents(&self, limit: i64) -> Result<Vec<PageContent>> {
        let query = ContentQuery::new()
            .with_state(ContentState::Published)
            .with_pagination(limit, 0);

        self.query_contents(&query).await
    }

    pub async fn get_popular_contents(&self, limit: i64) -> Result<Vec<PageContent>> {
        match &self.content_source {
            ContentSource::Database | ContentSource::Both => {
                // TODO: Implement get_popular_contents method
                Ok(vec![])
            }
            ContentSource::Files => {
                // For file-based content, we can't determine popularity
                self.get_recent_contents(limit).await
            }
        }
    }

    pub async fn get_all_tags(&self) -> Result<Vec<String>> {
        match &self.content_source {
            ContentSource::Database => {
                // TODO: Implement get_all_tags method
                Ok(vec![])
            }
            ContentSource::Files => {
                if let Some(loader) = &self.file_loader {
                    let contents = loader.load_published()?;
                    let mut tags = std::collections::HashSet::new();
                    for content in contents {
                        for tag in content.tags {
                            tags.insert(tag);
                        }
                    }
                    let mut sorted_tags: Vec<_> = tags.into_iter().collect();
                    sorted_tags.sort();
                    Ok(sorted_tags)
                } else {
                    Ok(Vec::new())
                }
            }
            ContentSource::Both => {
                // TODO: Implement get_all_tags method
                let mut db_tags = vec![];

                if let Some(loader) = &self.file_loader {
                    let contents = loader.load_published()?;
                    let mut file_tags = std::collections::HashSet::new();
                    for content in contents {
                        for tag in content.tags {
                            file_tags.insert(tag);
                        }
                    }

                    // Merge tags
                    let mut all_tags: std::collections::HashSet<_> = db_tags.into_iter().collect();
                    all_tags.extend(file_tags);

                    let mut sorted_tags: Vec<_> = all_tags.into_iter().collect();
                    sorted_tags.sort();
                    db_tags = sorted_tags;
                }

                Ok(db_tags)
            }
        }
    }

    pub async fn get_all_categories(&self) -> Result<Vec<String>> {
        match &self.content_source {
            ContentSource::Database => {
                // TODO: Implement get_all_categories method
                Ok(vec![])
            }
            ContentSource::Files => {
                if let Some(loader) = &self.file_loader {
                    let contents = loader.load_published()?;
                    let mut categories = std::collections::HashSet::new();
                    for content in contents {
                        if let Some(category) = content.category {
                            categories.insert(category);
                        }
                    }
                    let mut sorted_categories: Vec<_> = categories.into_iter().collect();
                    sorted_categories.sort();
                    Ok(sorted_categories)
                } else {
                    Ok(Vec::new())
                }
            }
            ContentSource::Both => {
                // TODO: Implement get_all_categories method
                let mut db_categories = vec![];

                if let Some(loader) = &self.file_loader {
                    let contents = loader.load_published()?;
                    let mut file_categories = std::collections::HashSet::new();
                    for content in contents {
                        if let Some(category) = content.category {
                            file_categories.insert(category);
                        }
                    }

                    // Merge categories
                    let mut all_categories: std::collections::HashSet<_> =
                        db_categories.into_iter().collect();
                    all_categories.extend(file_categories);

                    let mut sorted_categories: Vec<_> = all_categories.into_iter().collect();
                    sorted_categories.sort();
                    db_categories = sorted_categories;
                }

                Ok(db_categories)
            }
        }
    }

    pub async fn clear_cache(&self) -> Result<()> {
        if self.enable_cache {
            let mut cache = self.cache.write().await;
            cache.clear();
        }
        Ok(())
    }

    pub async fn reload_file_content(&self) -> Result<()> {
        if let Some(_loader) = &self.file_loader {
            // Clear cache to force reload
            self.clear_cache().await?;
            tracing::info!("File content cache cleared for reload");
        }
        Ok(())
    }

    pub async fn publish_scheduled_content(&self) -> Result<Vec<PageContent>> {
        match &self.content_source {
            ContentSource::Database | ContentSource::Both => {
                // TODO: Implement get_scheduled_contents method
                let scheduled_contents: Vec<PageContent> = vec![];
                for content in &scheduled_contents {
                    // TODO: Implement publish_scheduled_content method
                    // self.repository.publish_scheduled_content(&content.id).await?;

                    // Clear cache for the content
                    if self.enable_cache {
                        let mut cache = self.cache.write().await;
                        cache.remove(&content.slug);
                    }
                }
                Ok(scheduled_contents)
            }
            ContentSource::Files => {
                // File-based content doesn't support scheduled publishing
                Ok(Vec::new())
            }
        }
    }
}

#[derive(Debug, Clone, serde::Serialize)]
pub struct ContentStats {
    pub total_count: i64,
    pub published_count: i64,
    pub draft_count: i64,
}

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

    #[test]
    fn test_content_source_variants() {
        // Test that all ContentSource variants can be created
        let _database = ContentSource::Database;
        let _files = ContentSource::Files;
        let _both = ContentSource::Both;

        // Test matching
        assert!(matches!(ContentSource::Database, ContentSource::Database));
        assert!(matches!(ContentSource::Files, ContentSource::Files));
        assert!(matches!(ContentSource::Both, ContentSource::Both));
    }

    #[test]
    fn test_file_loader_creation() {
        let temp_dir = TempDir::new().unwrap();
        let loader = FileContentLoader::new(temp_dir.path());

        // Test that the loader can be created with extensions
        let loader_with_ext = loader.with_extensions(vec!["md".to_string(), "txt".to_string()]);

        // Just verify we can create the loader without errors
        assert!(true);
    }

    #[test]
    fn test_content_stats() {
        let stats = ContentStats {
            total_count: 100,
            published_count: 80,
            draft_count: 20,
        };

        assert_eq!(stats.total_count, 100);
        assert_eq!(stats.published_count, 80);
        assert_eq!(stats.draft_count, 20);
    }
}
