use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use uuid::Uuid;

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ContentType {
    Blog,
    Page,
    Article,
    Documentation,
    Tutorial,
    Custom(String),
}

impl ContentType {
    pub fn as_str(&self) -> &str {
        match self {
            ContentType::Blog => "blog",
            ContentType::Page => "page",
            ContentType::Article => "article",
            ContentType::Documentation => "documentation",
            ContentType::Tutorial => "tutorial",
            ContentType::Custom(s) => s,
        }
    }
}

impl From<String> for ContentType {
    fn from(s: String) -> Self {
        match s.as_str() {
            "blog" => ContentType::Blog,
            "page" => ContentType::Page,
            "article" => ContentType::Article,
            "documentation" => ContentType::Documentation,
            "tutorial" => ContentType::Tutorial,
            _ => ContentType::Custom(s),
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ContentState {
    Draft,
    Published,
    Archived,
    Scheduled,
}

impl ContentState {
    pub fn as_str(&self) -> &str {
        match self {
            ContentState::Draft => "draft",
            ContentState::Published => "published",
            ContentState::Archived => "archived",
            ContentState::Scheduled => "scheduled",
        }
    }
}

impl From<String> for ContentState {
    fn from(s: String) -> Self {
        match s.as_str() {
            "draft" => ContentState::Draft,
            "published" => ContentState::Published,
            "archived" => ContentState::Archived,
            "scheduled" => ContentState::Scheduled,
            _ => ContentState::Draft,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ContentFormat {
    Markdown,
    Html,
    PlainText,
}

impl ContentFormat {
    pub fn as_str(&self) -> &str {
        match self {
            ContentFormat::Markdown => "markdown",
            ContentFormat::Html => "html",
            ContentFormat::PlainText => "plaintext",
        }
    }
}

impl From<String> for ContentFormat {
    fn from(s: String) -> Self {
        match s.as_str() {
            "markdown" => ContentFormat::Markdown,
            "html" => ContentFormat::Html,
            "plaintext" => ContentFormat::PlainText,
            _ => ContentFormat::Markdown,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PageContent {
    pub id: Uuid,
    pub slug: String,
    pub title: String,
    pub name: String,
    pub author: Option<String>,
    pub author_id: Option<Uuid>,
    pub content_type: ContentType,
    pub content_format: ContentFormat,
    pub content: String,
    pub container: String,
    pub state: ContentState,
    pub require_login: bool,
    pub date_init: DateTime<Utc>,
    pub date_end: Option<DateTime<Utc>>,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
    pub published_at: Option<DateTime<Utc>>,
    pub metadata: HashMap<String, String>,
    pub tags: Vec<String>,
    pub category: Option<String>,
    pub featured_image: Option<String>,
    pub excerpt: Option<String>,
    pub seo_title: Option<String>,
    pub seo_description: Option<String>,
    pub allow_comments: bool,
    pub view_count: i64,
    pub sort_order: i32,
}

impl PageContent {
    pub fn new(
        slug: String,
        title: String,
        name: String,
        content_type: ContentType,
        content: String,
        container: String,
        author_id: Option<Uuid>,
    ) -> Self {
        let now = Utc::now();
        Self {
            id: Uuid::new_v4(),
            slug,
            title,
            name,
            author: None,
            author_id,
            content_type,
            content_format: ContentFormat::Markdown,
            content,
            container,
            state: ContentState::Draft,
            require_login: false,
            date_init: now,
            date_end: None,
            created_at: now,
            updated_at: now,
            published_at: None,
            metadata: HashMap::new(),
            tags: Vec::new(),
            category: None,
            featured_image: None,
            excerpt: None,
            seo_title: None,
            seo_description: None,
            allow_comments: true,
            view_count: 0,
            sort_order: 0,
        }
    }

    pub fn is_published(&self) -> bool {
        matches!(self.state, ContentState::Published)
    }

    pub fn is_accessible(&self, user_authenticated: bool) -> bool {
        if self.require_login && !user_authenticated {
            return false;
        }

        match self.state {
            ContentState::Published => true,
            ContentState::Scheduled => {
                if let Some(publish_date) = self.published_at {
                    Utc::now() >= publish_date
                } else {
                    false
                }
            }
            _ => false,
        }
    }

    pub fn is_active(&self) -> bool {
        let now = Utc::now();

        if now < self.date_init {
            return false;
        }

        if let Some(end_date) = self.date_end {
            if now > end_date {
                return false;
            }
        }

        true
    }

    pub fn should_display(&self, user_authenticated: bool) -> bool {
        self.is_accessible(user_authenticated) && self.is_active()
    }

    pub fn set_published(&mut self) {
        self.state = ContentState::Published;
        self.published_at = Some(Utc::now());
        self.updated_at = Utc::now();
    }

    pub fn set_scheduled(&mut self, publish_date: DateTime<Utc>) {
        self.state = ContentState::Scheduled;
        self.published_at = Some(publish_date);
        self.updated_at = Utc::now();
    }

    pub fn add_tag(&mut self, tag: String) {
        if !self.tags.contains(&tag) {
            self.tags.push(tag);
        }
    }

    pub fn remove_tag(&mut self, tag: &str) {
        self.tags.retain(|t| t != tag);
    }

    pub fn set_metadata(&mut self, key: String, value: String) {
        self.metadata.insert(key, value);
    }

    pub fn get_metadata(&self, key: &str) -> Option<&String> {
        self.metadata.get(key)
    }

    pub fn increment_view_count(&mut self) {
        self.view_count += 1;
    }

    // Builder methods
    pub fn with_content_format(mut self, format: ContentFormat) -> Self {
        self.content_format = format;
        self
    }

    pub fn with_state(mut self, state: ContentState) -> Self {
        self.state = state;
        self
    }

    pub fn with_author(mut self, author: String) -> Self {
        self.author = Some(author);
        self
    }

    pub fn with_tags(mut self, tags: Vec<String>) -> Self {
        self.tags = tags;
        self
    }

    pub fn with_category(mut self, category: String) -> Self {
        self.category = Some(category);
        self
    }

    pub fn with_excerpt(mut self, excerpt: String) -> Self {
        self.excerpt = Some(excerpt);
        self
    }

    pub fn with_featured_image(mut self, image: String) -> Self {
        self.featured_image = Some(image);
        self
    }

    pub fn with_require_login(mut self, require_login: bool) -> Self {
        self.require_login = require_login;
        self
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContentQuery {
    pub content_type: Option<ContentType>,
    pub state: Option<ContentState>,
    pub author_id: Option<Uuid>,
    pub category: Option<String>,
    pub tags: Option<Vec<String>>,
    pub require_login: Option<bool>,
    pub date_from: Option<DateTime<Utc>>,
    pub date_to: Option<DateTime<Utc>>,
    pub search: Option<String>,
    pub limit: Option<i64>,
    pub offset: Option<i64>,
    pub sort_by: Option<String>,
    pub sort_order: Option<String>,
}

impl ContentQuery {
    pub fn new() -> Self {
        Self {
            content_type: None,
            state: None,
            author_id: None,
            category: None,
            tags: None,
            require_login: None,
            date_from: None,
            date_to: None,
            search: None,
            limit: None,
            offset: None,
            sort_by: None,
            sort_order: None,
        }
    }

    pub fn with_content_type(mut self, content_type: ContentType) -> Self {
        self.content_type = Some(content_type);
        self
    }

    pub fn with_state(mut self, state: ContentState) -> Self {
        self.state = Some(state);
        self
    }

    pub fn with_author(mut self, author_id: Uuid) -> Self {
        self.author_id = Some(author_id);
        self
    }

    pub fn with_category(mut self, category: String) -> Self {
        self.category = Some(category);
        self
    }

    pub fn with_tags(mut self, tags: Vec<String>) -> Self {
        self.tags = Some(tags);
        self
    }

    pub fn with_pagination(mut self, limit: i64, offset: i64) -> Self {
        self.limit = Some(limit);
        self.offset = Some(offset);
        self
    }

    pub fn with_search(mut self, search: String) -> Self {
        self.search = Some(search);
        self
    }

    pub fn published_only(mut self) -> Self {
        self.state = Some(ContentState::Published);
        self
    }

    pub fn public_only(mut self) -> Self {
        self.require_login = Some(false);
        self
    }
}

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

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContentMetadata {
    pub title: String,
    pub description: Option<String>,
    pub author: Option<String>,
    pub keywords: Vec<String>,
    pub canonical_url: Option<String>,
    pub og_title: Option<String>,
    pub og_description: Option<String>,
    pub og_image: Option<String>,
    pub twitter_title: Option<String>,
    pub twitter_description: Option<String>,
    pub twitter_image: Option<String>,
    pub schema_type: Option<String>,
    pub reading_time: Option<i32>,
    pub word_count: Option<i32>,
}

impl ContentMetadata {
    pub fn new(title: String) -> Self {
        Self {
            title,
            description: None,
            author: None,
            keywords: Vec::new(),
            canonical_url: None,
            og_title: None,
            og_description: None,
            og_image: None,
            twitter_title: None,
            twitter_description: None,
            twitter_image: None,
            schema_type: None,
            reading_time: None,
            word_count: None,
        }
    }

    pub fn with_description(mut self, description: String) -> Self {
        self.description = Some(description);
        self
    }

    pub fn with_author(mut self, author: String) -> Self {
        self.author = Some(author);
        self
    }

    pub fn with_keywords(mut self, keywords: Vec<String>) -> Self {
        self.keywords = keywords;
        self
    }

    pub fn with_og_data(
        mut self,
        title: String,
        description: String,
        image: Option<String>,
    ) -> Self {
        self.og_title = Some(title);
        self.og_description = Some(description);
        self.og_image = image;
        self
    }

    pub fn with_twitter_data(
        mut self,
        title: String,
        description: String,
        image: Option<String>,
    ) -> Self {
        self.twitter_title = Some(title);
        self.twitter_description = Some(description);
        self.twitter_image = image;
        self
    }

    pub fn estimate_reading_time(&mut self, content: &str) {
        let words = content.split_whitespace().count();
        self.word_count = Some(words as i32);
        // Average reading speed: 200 words per minute
        self.reading_time = Some(((words as f32 / 200.0).ceil() as i32).max(1));
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContentTemplate {
    pub name: String,
    pub description: Option<String>,
    pub container: String,
    pub default_content_type: ContentType,
    pub default_format: ContentFormat,
    pub required_fields: Vec<String>,
    pub optional_fields: Vec<String>,
    pub default_metadata: HashMap<String, String>,
    pub supports_comments: bool,
    pub supports_tags: bool,
    pub supports_categories: bool,
    pub require_login_default: bool,
}

impl ContentTemplate {
    pub fn new(name: String, container: String) -> Self {
        Self {
            name,
            description: None,
            container,
            default_content_type: ContentType::Page,
            default_format: ContentFormat::Markdown,
            required_fields: vec!["title".to_string(), "content".to_string()],
            optional_fields: Vec::new(),
            default_metadata: HashMap::new(),
            supports_comments: true,
            supports_tags: true,
            supports_categories: true,
            require_login_default: false,
        }
    }

    pub fn blog_template() -> Self {
        Self {
            name: "Blog Post".to_string(),
            description: Some("Template for blog posts".to_string()),
            container: "blog-container".to_string(),
            default_content_type: ContentType::Blog,
            default_format: ContentFormat::Markdown,
            required_fields: vec![
                "title".to_string(),
                "content".to_string(),
                "author".to_string(),
            ],
            optional_fields: vec!["excerpt".to_string(), "featured_image".to_string()],
            default_metadata: HashMap::new(),
            supports_comments: true,
            supports_tags: true,
            supports_categories: true,
            require_login_default: false,
        }
    }

    pub fn page_template() -> Self {
        Self {
            name: "Static Page".to_string(),
            description: Some("Template for static pages".to_string()),
            container: "page-container".to_string(),
            default_content_type: ContentType::Page,
            default_format: ContentFormat::Markdown,
            required_fields: vec!["title".to_string(), "content".to_string()],
            optional_fields: vec!["seo_title".to_string(), "seo_description".to_string()],
            default_metadata: HashMap::new(),
            supports_comments: false,
            supports_tags: false,
            supports_categories: false,
            require_login_default: false,
        }
    }

    pub fn documentation_template() -> Self {
        Self {
            name: "Documentation".to_string(),
            description: Some("Template for documentation pages".to_string()),
            container: "docs-container".to_string(),
            default_content_type: ContentType::Documentation,
            default_format: ContentFormat::Markdown,
            required_fields: vec!["title".to_string(), "content".to_string()],
            optional_fields: vec!["category".to_string()],
            default_metadata: HashMap::new(),
            supports_comments: false,
            supports_tags: true,
            supports_categories: true,
            require_login_default: false,
        }
    }
}

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

    #[test]
    fn test_page_content_creation() {
        let content = PageContent::new(
            "test-page".to_string(),
            "Test Page".to_string(),
            "test-page".to_string(),
            ContentType::Page,
            "# Test Content".to_string(),
            "page-container".to_string(),
            None,
        );

        assert_eq!(content.slug, "test-page");
        assert_eq!(content.title, "Test Page");
        assert_eq!(content.content_type, ContentType::Page);
        assert_eq!(content.state, ContentState::Draft);
        assert!(!content.require_login);
    }

    #[test]
    fn test_content_accessibility() {
        let mut content = PageContent::new(
            "test".to_string(),
            "Test".to_string(),
            "test".to_string(),
            ContentType::Page,
            "Content".to_string(),
            "container".to_string(),
            None,
        );

        // Draft content should not be accessible
        assert!(!content.is_accessible(false));
        assert!(!content.is_accessible(true));

        // Published content should be accessible
        content.set_published();
        assert!(content.is_accessible(false));
        assert!(content.is_accessible(true));

        // Login required content
        content.require_login = true;
        assert!(!content.is_accessible(false));
        assert!(content.is_accessible(true));
    }

    #[test]
    fn test_content_query_builder() {
        let query = ContentQuery::new()
            .with_content_type(ContentType::Blog)
            .with_state(ContentState::Published)
            .with_pagination(10, 0)
            .public_only();

        assert_eq!(query.content_type, Some(ContentType::Blog));
        assert_eq!(query.state, Some(ContentState::Published));
        assert_eq!(query.limit, Some(10));
        assert_eq!(query.offset, Some(0));
        assert_eq!(query.require_login, Some(false));
    }

    #[test]
    fn test_content_metadata() {
        let mut metadata = ContentMetadata::new("Test Title".to_string())
            .with_description("Test description".to_string())
            .with_author("Test Author".to_string());

        metadata.estimate_reading_time(
            "This is a test content with some words to estimate reading time.",
        );

        assert_eq!(metadata.title, "Test Title");
        assert_eq!(metadata.description, Some("Test description".to_string()));
        assert_eq!(metadata.author, Some("Test Author".to_string()));
        assert!(metadata.reading_time.is_some());
        assert!(metadata.word_count.is_some());
    }

    #[test]
    fn test_content_templates() {
        let blog_template = ContentTemplate::blog_template();
        assert_eq!(blog_template.name, "Blog Post");
        assert_eq!(blog_template.default_content_type, ContentType::Blog);
        assert!(blog_template.supports_comments);
        assert!(blog_template.supports_tags);

        let page_template = ContentTemplate::page_template();
        assert_eq!(page_template.name, "Static Page");
        assert_eq!(page_template.default_content_type, ContentType::Page);
        assert!(!page_template.supports_comments);
        assert!(!page_template.supports_tags);
    }

    #[test]
    fn test_content_type_conversion() {
        let content_type = ContentType::from("blog".to_string());
        assert_eq!(content_type, ContentType::Blog);

        let custom_type = ContentType::from("custom_type".to_string());
        assert_eq!(custom_type, ContentType::Custom("custom_type".to_string()));
    }
}
