use anyhow::Result;
use chrono::{DateTime, Utc};
use shared::content::{ContentFormat, ContentQuery, ContentState, ContentType, PageContent};
use std::collections::HashMap;
use uuid::Uuid;

use crate::database::{
    DatabaseType,
    connection::{DatabaseConnection, DatabaseRow},
};

#[derive(Debug)]
pub struct ContentRow {
    pub id: Uuid,
    pub slug: String,
    pub title: String,
    pub name: String,
    pub author: Option<String>,
    pub author_id: Option<Uuid>,
    pub content_type: String,
    pub content_format: String,
    pub content: String,
    pub container: String,
    pub state: String,
    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: serde_json::Value,
    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 From<ContentRow> for PageContent {
    fn from(row: ContentRow) -> Self {
        let metadata: HashMap<String, String> = row
            .metadata
            .as_object()
            .map(|obj| {
                obj.iter()
                    .filter_map(|(k, v)| v.as_str().map(|s| (k.clone(), s.to_string())))
                    .collect()
            })
            .unwrap_or_else(|| HashMap::new());

        PageContent {
            id: row.id,
            slug: row.slug,
            title: row.title,
            name: row.name,
            author: row.author,
            author_id: row.author_id,
            content_type: ContentType::from(row.content_type),
            content_format: ContentFormat::from(row.content_format),
            content: row.content,
            container: row.container,
            state: ContentState::from(row.state),
            require_login: row.require_login,
            date_init: row.date_init,
            date_end: row.date_end,
            created_at: row.created_at,
            updated_at: row.updated_at,
            published_at: row.published_at,
            metadata,
            tags: row.tags,
            category: row.category,
            featured_image: row.featured_image,
            excerpt: row.excerpt,
            seo_title: row.seo_title,
            seo_description: row.seo_description,
            allow_comments: row.allow_comments,
            view_count: row.view_count,
            sort_order: row.sort_order,
        }
    }
}

pub struct ContentRepository {
    database: DatabaseConnection,
}

impl ContentRepository {
    pub fn new(database: DatabaseConnection) -> Self {
        Self { database }
    }

    pub fn from_pool(pool: &crate::database::DatabasePool) -> Self {
        let connection = DatabaseConnection::from_pool(pool);
        Self::new(connection)
    }

    pub async fn create_content(&self, content: &PageContent) -> Result<()> {
        match self.database.database_type() {
            DatabaseType::PostgreSQL => self.create_content_postgres(content).await,
            DatabaseType::SQLite => self.create_content_sqlite(content).await,
        }
    }

    async fn create_content_postgres(&self, content: &PageContent) -> Result<()> {
        let metadata = serde_json::to_value(&content.metadata)?;

        self.database
            .execute(
                r#"
                INSERT INTO page_contents (
                    id, slug, title, name, author, author_id, content_type, content_format,
                    content, container, state, require_login, date_init, date_end,
                    created_at, updated_at, published_at, metadata, tags, category,
                    featured_image, excerpt, seo_title, seo_description, allow_comments,
                    view_count, sort_order
                ) VALUES (
                    $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14,
                    $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27
                )
                "#,
                &[
                    content.id.into(),
                    content.slug.clone().into(),
                    content.title.clone().into(),
                    content.name.clone().into(),
                    content.author.clone().into(),
                    content.author_id.into(),
                    content.content_type.as_str().to_string().into(),
                    content.content_format.as_str().to_string().into(),
                    content.content.clone().into(),
                    content.container.clone().into(),
                    content.state.as_str().to_string().into(),
                    content.require_login.into(),
                    content.date_init.into(),
                    content.date_end.into(),
                    content.created_at.into(),
                    content.updated_at.into(),
                    content.published_at.into(),
                    metadata.to_string().into(),
                    serde_json::to_string(&content.tags)
                        .map_err(|e| anyhow::anyhow!("Failed to serialize tags: {}", e))?
                        .into(),
                    content.category.clone().into(),
                    content.featured_image.clone().into(),
                    content.excerpt.clone().into(),
                    content.seo_title.clone().into(),
                    content.seo_description.clone().into(),
                    content.allow_comments.into(),
                    content.view_count.into(),
                    content.sort_order.into(),
                ],
            )
            .await?;

        Ok(())
    }

    async fn create_content_sqlite(&self, content: &PageContent) -> Result<()> {
        let metadata = serde_json::to_value(&content.metadata)?;
        let tags_json = serde_json::to_string(&content.tags)?;

        self.database
            .execute(
                r#"
                INSERT INTO page_contents (
                    id, slug, title, name, author, author_id, content_type, content_format,
                    content, container, state, require_login, date_init, date_end,
                    created_at, updated_at, published_at, metadata, tags, category,
                    featured_image, excerpt, seo_title, seo_description, allow_comments,
                    view_count, sort_order
                ) VALUES (
                    ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
                )
                "#,
                &[
                    content.id.to_string().into(),
                    content.slug.clone().into(),
                    content.title.clone().into(),
                    content.name.clone().into(),
                    content.author.clone().into(),
                    content.author_id.map(|id| id.to_string()).into(),
                    content.content_type.as_str().to_string().into(),
                    content.content_format.as_str().to_string().into(),
                    content.content.clone().into(),
                    content.container.clone().into(),
                    content.state.as_str().to_string().into(),
                    content.require_login.into(),
                    content.date_init.to_rfc3339().into(),
                    content.date_end.map(|d| d.to_rfc3339()).into(),
                    content.created_at.to_rfc3339().into(),
                    content.updated_at.to_rfc3339().into(),
                    content.published_at.map(|d| d.to_rfc3339()).into(),
                    metadata.to_string().into(),
                    tags_json.into(),
                    content.category.clone().into(),
                    content.featured_image.clone().into(),
                    content.excerpt.clone().into(),
                    content.seo_title.clone().into(),
                    content.seo_description.clone().into(),
                    content.allow_comments.into(),
                    content.view_count.into(),
                    content.sort_order.into(),
                ],
            )
            .await?;

        Ok(())
    }

    pub async fn get_content_by_slug(&self, slug: &str) -> Result<Option<PageContent>> {
        match self.database.database_type() {
            DatabaseType::PostgreSQL => self.get_content_by_slug_postgres(slug).await,
            DatabaseType::SQLite => self.get_content_by_slug_sqlite(slug).await,
        }
    }

    async fn get_content_by_slug_postgres(&self, slug: &str) -> Result<Option<PageContent>> {
        let row = self
            .database
            .fetch_optional(
                r#"
                SELECT id, slug, title, name, author, author_id, content_type, content_format,
                       content, container, state, require_login, date_init, date_end,
                       created_at, updated_at, published_at, metadata, tags, category,
                       featured_image, excerpt, seo_title, seo_description, allow_comments,
                       view_count, sort_order
                FROM page_contents
                WHERE slug = $1
                "#,
                &[slug.into()],
            )
            .await?;

        if let Some(row) = row {
            Ok(Some(PageContent {
                id: row.get_uuid("id")?,
                slug: row.get_string("slug")?,
                title: row.get_string("title")?,
                name: row.get_string("name")?,
                author: row.get_optional_string("author")?,
                author_id: row.get_optional_uuid("author_id")?,
                content_type: ContentType::from(row.get_string("content_type")?),
                content_format: ContentFormat::from(row.get_string("content_format")?),
                content: row.get_string("content")?,
                container: row.get_string("container")?,
                state: ContentState::from(row.get_string("state")?),
                require_login: row.get_bool("require_login")?,
                date_init: row.get_datetime("date_init")?,
                date_end: row.get_optional_datetime("date_end")?,
                created_at: row.get_datetime("created_at")?,
                updated_at: row.get_datetime("updated_at")?,
                published_at: row.get_optional_datetime("published_at")?,
                metadata: serde_json::from_str(&row.get_string("metadata")?)
                    .unwrap_or_else(|_| HashMap::new()),
                tags: serde_json::from_str(&row.get_string("tags")?).unwrap_or_else(|_| Vec::new()),
                category: row.get_optional_string("category")?,
                featured_image: row.get_optional_string("featured_image")?,
                excerpt: row.get_optional_string("excerpt")?,
                seo_title: row.get_optional_string("seo_title")?,
                seo_description: row.get_optional_string("seo_description")?,
                allow_comments: row.get_bool("allow_comments")?,
                view_count: row.get_i64("view_count")?,
                sort_order: row.get_i32("sort_order")?,
            }))
        } else {
            Ok(None)
        }
    }

    async fn get_content_by_slug_sqlite(&self, slug: &str) -> Result<Option<PageContent>> {
        let row = self
            .database
            .fetch_optional(
                r#"
                SELECT id, slug, title, name, author, author_id, content_type, content_format,
                       content, container, state, require_login, date_init, date_end,
                       created_at, updated_at, published_at, metadata, tags, category,
                       featured_image, excerpt, seo_title, seo_description, allow_comments,
                       view_count, sort_order
                FROM page_contents
                WHERE slug = ?
                "#,
                &[slug.into()],
            )
            .await?;

        if let Some(row) = row {
            Ok(Some(PageContent {
                id: row.get_uuid("id")?,
                slug: row.get_string("slug")?,
                title: row.get_string("title")?,
                name: row.get_string("name")?,
                author: row.get_optional_string("author")?,
                author_id: row.get_optional_uuid("author_id")?,
                content_type: ContentType::from(row.get_string("content_type")?),
                content_format: ContentFormat::from(row.get_string("content_format")?),
                content: row.get_string("content")?,
                container: row.get_string("container")?,
                state: ContentState::from(row.get_string("state")?),
                require_login: row.get_bool("require_login")?,
                date_init: row.get_datetime("date_init")?,
                date_end: row.get_optional_datetime("date_end")?,
                created_at: row.get_datetime("created_at")?,
                updated_at: row.get_datetime("updated_at")?,
                published_at: row.get_optional_datetime("published_at")?,
                metadata: serde_json::from_str(&row.get_string("metadata")?)
                    .unwrap_or_else(|_| HashMap::new()),
                tags: serde_json::from_str(&row.get_string("tags")?).unwrap_or_else(|_| Vec::new()),
                category: row.get_optional_string("category")?,
                featured_image: row.get_optional_string("featured_image")?,
                excerpt: row.get_optional_string("excerpt")?,
                seo_title: row.get_optional_string("seo_title")?,
                seo_description: row.get_optional_string("seo_description")?,
                allow_comments: row.get_bool("allow_comments")?,
                view_count: row.get_i64("view_count")?,
                sort_order: row.get_i32("sort_order")?,
            }))
        } else {
            Ok(None)
        }
    }

    pub async fn get_content_by_id(&self, id: &Uuid) -> Result<Option<PageContent>> {
        match self.database.database_type() {
            DatabaseType::PostgreSQL => self.get_content_by_id_postgres(id).await,
            DatabaseType::SQLite => self.get_content_by_id_sqlite(id).await,
        }
    }

    async fn get_content_by_id_postgres(&self, id: &Uuid) -> Result<Option<PageContent>> {
        let row = self
            .database
            .fetch_optional(
                r#"
                SELECT id, slug, title, name, author, author_id, content_type, content_format,
                       content, container, state, require_login, date_init, date_end,
                       created_at, updated_at, published_at, metadata, tags, category,
                       featured_image, excerpt, seo_title, seo_description, allow_comments,
                       view_count, sort_order
                FROM page_contents
                WHERE id = $1
                "#,
                &[(*id).into()],
            )
            .await?;

        if let Some(row) = row {
            Ok(Some(self.row_to_content(row)?))
        } else {
            Ok(None)
        }
    }

    async fn get_content_by_id_sqlite(&self, id: &Uuid) -> Result<Option<PageContent>> {
        let row = self
            .database
            .fetch_optional(
                r#"
                SELECT id, slug, title, name, author, author_id, content_type, content_format,
                       content, container, state, require_login, date_init, date_end,
                       created_at, updated_at, published_at, metadata, tags, category,
                       featured_image, excerpt, seo_title, seo_description, allow_comments,
                       view_count, sort_order
                FROM page_contents
                WHERE id = ?
                "#,
                &[id.to_string().into()],
            )
            .await?;

        if let Some(row) = row {
            Ok(Some(self.row_to_content(row)?))
        } else {
            Ok(None)
        }
    }

    pub async fn list_content(&self, query: &ContentQuery) -> Result<Vec<PageContent>> {
        match self.database.database_type() {
            DatabaseType::PostgreSQL => self.list_content_postgres(query).await,
            DatabaseType::SQLite => self.list_content_sqlite(query).await,
        }
    }

    async fn list_content_postgres(&self, query: &ContentQuery) -> Result<Vec<PageContent>> {
        let mut sql = String::from(
            r#"
            SELECT id, slug, title, name, author, author_id, content_type, content_format,
                   content, container, state, require_login, date_init, date_end,
                   created_at, updated_at, published_at, metadata, tags, category,
                   featured_image, excerpt, seo_title, seo_description, allow_comments,
                   view_count, sort_order
            FROM page_contents
            WHERE 1=1
            "#,
        );

        let mut params = Vec::new();
        let mut param_count = 1;

        if let Some(ref content_type) = query.content_type {
            sql.push_str(&format!(" AND content_type = ${}", param_count));
            params.push(content_type.as_str().to_string().into());
            param_count += 1;
        }

        if let Some(ref state) = query.state {
            sql.push_str(&format!(" AND state = ${}", param_count));
            params.push(state.as_str().to_string().into());
            param_count += 1;
        }

        if let Some(ref category) = query.category {
            sql.push_str(&format!(" AND category = ${}", param_count));
            params.push(category.clone().into());
            param_count += 1;
        }

        if let Some(ref author_id) = query.author_id {
            sql.push_str(&format!(" AND author_id = ${}", param_count));
            params.push((*author_id).into());
            param_count += 1;
        }

        sql.push_str(" ORDER BY sort_order ASC, created_at DESC");

        if let Some(limit) = query.limit {
            sql.push_str(&format!(" LIMIT ${}", param_count));
            params.push((limit as i64).into());
            param_count += 1;
        }

        if let Some(offset) = query.offset {
            sql.push_str(&format!(" OFFSET ${}", param_count));
            params.push((offset as i64).into());
        }

        let rows = self.database.fetch_all(&sql, &params).await?;

        let mut contents = Vec::new();
        for row in rows {
            contents.push(self.row_to_content(row)?);
        }

        Ok(contents)
    }

    async fn list_content_sqlite(&self, query: &ContentQuery) -> Result<Vec<PageContent>> {
        let mut sql = String::from(
            r#"
            SELECT id, slug, title, name, author, author_id, content_type, content_format,
                   content, container, state, require_login, date_init, date_end,
                   created_at, updated_at, published_at, metadata, tags, category,
                   featured_image, excerpt, seo_title, seo_description, allow_comments,
                   view_count, sort_order
            FROM page_contents
            WHERE 1=1
            "#,
        );

        let mut params = Vec::new();

        if let Some(ref content_type) = query.content_type {
            sql.push_str(" AND content_type = ?");
            params.push(content_type.as_str().to_string().into());
        }

        if let Some(ref state) = query.state {
            sql.push_str(" AND state = ?");
            params.push(state.as_str().to_string().into());
        }

        if let Some(ref category) = query.category {
            sql.push_str(" AND category = ?");
            params.push(category.clone().into());
        }

        if let Some(ref author_id) = query.author_id {
            sql.push_str(" AND author_id = ?");
            params.push(author_id.to_string().into());
        }

        sql.push_str(" ORDER BY sort_order ASC, created_at DESC");

        if let Some(limit) = query.limit {
            sql.push_str(" LIMIT ?");
            params.push((limit as i64).into());
        }

        if let Some(offset) = query.offset {
            sql.push_str(" OFFSET ?");
            params.push((offset as i64).into());
        }

        let rows = self.database.fetch_all(&sql, &params).await?;

        let mut contents = Vec::new();
        for row in rows {
            contents.push(self.row_to_content(row)?);
        }

        Ok(contents)
    }

    pub async fn update_content(&self, content: &PageContent) -> Result<()> {
        match self.database.database_type() {
            DatabaseType::PostgreSQL => self.update_content_postgres(content).await,
            DatabaseType::SQLite => self.update_content_sqlite(content).await,
        }
    }

    async fn update_content_postgres(&self, content: &PageContent) -> Result<()> {
        let metadata = serde_json::to_value(&content.metadata)?;

        self.database
            .execute(
                r#"
                UPDATE page_contents SET
                    slug = $2, title = $3, name = $4, author = $5, author_id = $6,
                    content_type = $7, content_format = $8, content = $9, container = $10,
                    state = $11, require_login = $12, date_init = $13, date_end = $14,
                    updated_at = $15, published_at = $16, metadata = $17, tags = $18,
                    category = $19, featured_image = $20, excerpt = $21, seo_title = $22,
                    seo_description = $23, allow_comments = $24, view_count = $25, sort_order = $26
                WHERE id = $1
                "#,
                &[
                    content.id.into(),
                    content.slug.clone().into(),
                    content.title.clone().into(),
                    content.name.clone().into(),
                    content.author.clone().into(),
                    content.author_id.into(),
                    content.content_type.as_str().to_string().into(),
                    content.content_format.as_str().to_string().into(),
                    content.content.clone().into(),
                    content.container.clone().into(),
                    content.state.as_str().to_string().into(),
                    content.require_login.into(),
                    content.date_init.into(),
                    content.date_end.into(),
                    content.updated_at.into(),
                    content.published_at.into(),
                    metadata.to_string().into(),
                    serde_json::to_string(&content.tags)
                        .map_err(|e| anyhow::anyhow!("Failed to serialize tags: {}", e))?
                        .into(),
                    content.category.clone().into(),
                    content.featured_image.clone().into(),
                    content.excerpt.clone().into(),
                    content.seo_title.clone().into(),
                    content.seo_description.clone().into(),
                    content.allow_comments.into(),
                    content.view_count.into(),
                    content.sort_order.into(),
                ],
            )
            .await?;

        Ok(())
    }

    async fn update_content_sqlite(&self, content: &PageContent) -> Result<()> {
        let metadata = serde_json::to_value(&content.metadata)?;
        let tags_json = serde_json::to_string(&content.tags)?;

        self.database
            .execute(
                r#"
                UPDATE page_contents SET
                    slug = ?, title = ?, name = ?, author = ?, author_id = ?,
                    content_type = ?, content_format = ?, content = ?, container = ?,
                    state = ?, require_login = ?, date_init = ?, date_end = ?,
                    updated_at = ?, published_at = ?, metadata = ?, tags = ?,
                    category = ?, featured_image = ?, excerpt = ?, seo_title = ?,
                    seo_description = ?, allow_comments = ?, view_count = ?, sort_order = ?
                WHERE id = ?
                "#,
                &[
                    content.slug.clone().into(),
                    content.title.clone().into(),
                    content.name.clone().into(),
                    content.author.clone().into(),
                    content.author_id.map(|id| id.to_string()).into(),
                    content.content_type.as_str().to_string().into(),
                    content.content_format.as_str().to_string().into(),
                    content.content.clone().into(),
                    content.container.clone().into(),
                    content.state.as_str().to_string().into(),
                    content.require_login.into(),
                    content.date_init.to_rfc3339().into(),
                    content.date_end.map(|d| d.to_rfc3339()).into(),
                    content.updated_at.to_rfc3339().into(),
                    content.published_at.map(|d| d.to_rfc3339()).into(),
                    metadata.to_string().into(),
                    tags_json.into(),
                    content.category.clone().into(),
                    content.featured_image.clone().into(),
                    content.excerpt.clone().into(),
                    content.seo_title.clone().into(),
                    content.seo_description.clone().into(),
                    content.allow_comments.into(),
                    content.view_count.into(),
                    content.sort_order.into(),
                    content.id.to_string().into(),
                ],
            )
            .await?;

        Ok(())
    }

    pub async fn delete_content(&self, id: &Uuid) -> Result<()> {
        match self.database.database_type() {
            DatabaseType::PostgreSQL => self.delete_content_postgres(id).await,
            DatabaseType::SQLite => self.delete_content_sqlite(id).await,
        }
    }

    async fn delete_content_postgres(&self, id: &Uuid) -> Result<()> {
        self.database
            .execute("DELETE FROM page_contents WHERE id = $1", &[(*id).into()])
            .await?;
        Ok(())
    }

    async fn delete_content_sqlite(&self, id: &Uuid) -> Result<()> {
        self.database
            .execute(
                "DELETE FROM page_contents WHERE id = ?",
                &[id.to_string().into()],
            )
            .await?;
        Ok(())
    }

    fn row_to_content(&self, row: DatabaseRow) -> Result<PageContent> {
        Ok(PageContent {
            id: row.get_uuid("id")?,
            slug: row.get_string("slug")?,
            title: row.get_string("title")?,
            name: row.get_string("name")?,
            author: row.get_optional_string("author")?,
            author_id: row.get_optional_uuid("author_id")?,
            content_type: ContentType::from(row.get_string("content_type")?),
            content_format: ContentFormat::from(row.get_string("content_format")?),
            content: row.get_string("content")?,
            container: row.get_string("container")?,
            state: ContentState::from(row.get_string("state")?),
            require_login: row.get_bool("require_login")?,
            date_init: row.get_datetime("date_init")?,
            date_end: row.get_optional_datetime("date_end")?,
            created_at: row.get_datetime("created_at")?,
            updated_at: row.get_datetime("updated_at")?,
            published_at: row.get_optional_datetime("published_at")?,
            metadata: serde_json::from_str(&row.get_string("metadata")?)
                .unwrap_or_else(|_| HashMap::new()),
            tags: serde_json::from_str(&row.get_string("tags")?).unwrap_or_else(|_| Vec::new()),
            category: row.get_optional_string("category")?,
            featured_image: row.get_optional_string("featured_image")?,
            excerpt: row.get_optional_string("excerpt")?,
            seo_title: row.get_optional_string("seo_title")?,
            seo_description: row.get_optional_string("seo_description")?,
            allow_comments: row.get_bool("allow_comments")?,
            view_count: row.get_i64("view_count")?,
            sort_order: row.get_i32("sort_order")?,
        })
    }
}
