use axum::{
    Router,
    extract::{Path, Query, State},
    response::{Html, IntoResponse, Json},
    routing::{get, post},
};
use serde::{Deserialize, Serialize};
use shared::content::{ContentQuery, ContentState, ContentType, PageContent};
use std::collections::HashMap;
use std::sync::Arc;
use uuid::Uuid;

use super::{ContentRenderer, ContentService, TocEntry};

#[derive(Debug, Serialize)]
pub struct ApiResponse<T> {
    pub success: bool,
    pub data: Option<T>,
    pub message: Option<String>,
    pub errors: Option<Vec<String>>,
}

impl<T> ApiResponse<T> {
    pub fn success(data: T) -> Self {
        Self {
            success: true,
            data: Some(data),
            message: None,
            errors: None,
        }
    }
}

impl ApiResponse<()> {
    pub fn error<U>(message: String) -> ApiResponse<U> {
        ApiResponse {
            success: false,
            data: None,
            message: Some(message),
            errors: None,
        }
    }

    #[allow(dead_code)]
    pub fn validation_error<U>(errors: Vec<String>) -> ApiResponse<U> {
        ApiResponse {
            success: false,
            data: None,
            message: Some("Validation failed".to_string()),
            errors: Some(errors),
        }
    }
}

#[derive(Debug, Deserialize)]
pub struct ContentQueryParams {
    pub content_type: Option<String>,
    pub state: Option<String>,
    pub author_id: Option<String>,
    pub category: Option<String>,
    pub tags: Option<String>,
    pub require_login: Option<bool>,
    pub search: Option<String>,
    pub limit: Option<i64>,
    pub offset: Option<i64>,
    pub sort_by: Option<String>,
    pub sort_order: Option<String>,
}

impl From<ContentQueryParams> for ContentQuery {
    fn from(params: ContentQueryParams) -> Self {
        let mut query = ContentQuery::new();

        if let Some(content_type) = params.content_type {
            query.content_type = Some(ContentType::from(content_type));
        }

        if let Some(state) = params.state {
            query.state = Some(ContentState::from(state));
        }

        if let Some(author_id) = params.author_id {
            if let Ok(uuid) = Uuid::parse_str(&author_id) {
                query.author_id = Some(uuid);
            }
        }

        if let Some(category) = params.category {
            query.category = Some(category);
        }

        if let Some(tags) = params.tags {
            let tag_list: Vec<String> = tags.split(',').map(|s| s.trim().to_string()).collect();
            query.tags = Some(tag_list);
        }

        query.require_login = params.require_login;
        query.search = params.search;
        query.limit = params.limit;
        query.offset = params.offset;
        query.sort_by = params.sort_by;
        query.sort_order = params.sort_order;

        query
    }
}

#[derive(Debug, Serialize)]
pub struct ContentResponse {
    pub content: PageContent,
    pub rendered_html: String,
    pub table_of_contents: Vec<TocEntry>,
    pub excerpt: String,
    pub reading_time: Option<i32>,
}

#[derive(Debug, Serialize)]
pub struct ContentListResponse {
    pub contents: Vec<PageContent>,
    pub total_count: i64,
    pub has_more: bool,
}

#[derive(Debug, Deserialize)]
pub struct CreateContentRequest {
    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: Option<String>,
    pub content: String,
    pub container: String,
    pub state: Option<String>,
    pub require_login: Option<bool>,
    pub tags: Option<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: Option<bool>,
    pub sort_order: Option<i32>,
    pub metadata: Option<HashMap<String, String>>,
}

pub fn create_content_routes() -> Router<Arc<ContentService>> {
    Router::new()
        .route("/contents", get(list_contents).post(create_content))
        .route("/contents/:id", get(get_content_by_id))
        .route("/contents/slug/:slug", get(get_content_by_slug))
        .route("/contents/slug/:slug/render", get(render_content_by_slug))
        .route("/contents/search", get(search_contents))
        .route("/contents/published", get(get_published_contents))
        .route("/contents/stats", get(get_content_stats))
        .route("/contents/tags", get(get_all_tags))
        .route("/contents/categories", get(get_all_categories))
        .route("/contents/type/:content_type", get(get_contents_by_type))
        .route(
            "/contents/category/:category",
            get(get_contents_by_category),
        )
        .route("/contents/author/:author_id", get(get_contents_by_author))
        .route("/contents/recent", get(get_recent_contents))
        .route("/contents/popular", get(get_popular_contents))
        .route("/contents/:id/increment-view", post(increment_view_count))
        .route("/contents/:id/render", get(render_content_by_id))
        .route("/contents/:id/toc", get(get_table_of_contents))
        .route("/contents/reload", post(reload_content))
        .route(
            "/contents/publish-scheduled",
            post(publish_scheduled_content),
        )
}

pub async fn list_contents(
    State(service): State<Arc<ContentService>>,
    Query(params): Query<ContentQueryParams>,
) -> impl IntoResponse {
    let query = ContentQuery::from(params);

    match service.query_contents(&query).await {
        Ok(contents) => {
            let total_count = contents.len() as i64;
            let has_more = query.limit.map_or(false, |limit| total_count >= limit);

            Json(ApiResponse::success(ContentListResponse {
                contents,
                total_count,
                has_more,
            }))
        }
        Err(e) => {
            tracing::error!("Failed to list contents: {}", e);
            Json(ApiResponse::error(
                "Failed to retrieve contents".to_string(),
            ))
        }
    }
}

pub async fn get_content_by_id(
    State(service): State<Arc<ContentService>>,
    Path(id): Path<Uuid>,
) -> impl IntoResponse {
    match service.get_content_by_id(id).await {
        Ok(Some(content)) => Json(ApiResponse::success(content)),
        Ok(None) => Json(ApiResponse::error("Content not found".to_string())),
        Err(e) => {
            tracing::error!("Failed to get content by ID {}: {}", id, e);
            Json(ApiResponse::error("Failed to retrieve content".to_string()))
        }
    }
}

pub async fn get_content_by_slug(
    State(service): State<Arc<ContentService>>,
    Path(slug): Path<String>,
) -> impl IntoResponse {
    match service.get_content_by_slug(&slug).await {
        Ok(Some(content)) => Json(ApiResponse::success(content)),
        Ok(None) => Json(ApiResponse::error("Content not found".to_string())),
        Err(e) => {
            tracing::error!("Failed to get content by slug {}: {}", slug, e);
            Json(ApiResponse::error("Failed to retrieve content".to_string()))
        }
    }
}

pub async fn render_content_by_slug(
    State(service): State<Arc<ContentService>>,
    Path(slug): Path<String>,
) -> impl IntoResponse {
    match service.get_content_by_slug(&slug).await {
        Ok(Some(content)) => {
            let renderer = ContentRenderer::new();

            match renderer.render_content(&content) {
                Ok(rendered_html) => {
                    let table_of_contents = renderer
                        .generate_table_of_contents(&content)
                        .unwrap_or_default();
                    let excerpt = renderer.extract_excerpt(&content, 200);

                    // Calculate reading time (rough estimate: 200 words per minute)
                    let word_count = content.content.split_whitespace().count();
                    let reading_time = Some(((word_count as f32 / 200.0).ceil() as i32).max(1));

                    Json(ApiResponse::success(ContentResponse {
                        content,
                        rendered_html,
                        table_of_contents,
                        excerpt,
                        reading_time,
                    }))
                }
                Err(e) => {
                    tracing::error!("Failed to render content: {}", e);
                    Json(ApiResponse::error("Failed to render content".to_string()))
                }
            }
        }
        Ok(None) => Json(ApiResponse::error("Content not found".to_string())),
        Err(e) => {
            tracing::error!("Failed to get content by slug {}: {}", slug, e);
            Json(ApiResponse::error("Failed to retrieve content".to_string()))
        }
    }
}

pub async fn render_content_by_id(
    State(service): State<Arc<ContentService>>,
    Path(id): Path<Uuid>,
) -> impl IntoResponse {
    match service.get_content_by_id(id).await {
        Ok(Some(content)) => {
            let renderer = ContentRenderer::new();

            match renderer.render_content(&content) {
                Ok(rendered_html) => Html(rendered_html),
                Err(e) => {
                    tracing::error!("Failed to render content: {}", e);
                    Html("<p>Error rendering content</p>".to_string())
                }
            }
        }
        Ok(None) => Html("<p>Content not found</p>".to_string()),
        Err(e) => {
            tracing::error!("Failed to get content by ID {}: {}", id, e);
            Html("<p>Error retrieving content</p>".to_string())
        }
    }
}

pub async fn create_content(
    State(service): State<Arc<ContentService>>,
    Json(request): Json<CreateContentRequest>,
) -> impl IntoResponse {
    let content_type = ContentType::from(request.content_type);
    let content_format = request
        .content_format
        .map(shared::content::ContentFormat::from)
        .unwrap_or(shared::content::ContentFormat::Markdown);
    let state = request
        .state
        .map(ContentState::from)
        .unwrap_or(ContentState::Draft);

    let mut content = PageContent::new(
        request.slug,
        request.title,
        request.name,
        content_type,
        request.content,
        request.container,
        request.author_id,
    );

    content.author = request.author;
    content.content_format = content_format;
    content.state = state;
    content.require_login = request.require_login.unwrap_or(false);
    content.tags = request.tags.unwrap_or_default();
    content.category = request.category;
    content.featured_image = request.featured_image;
    content.excerpt = request.excerpt;
    content.seo_title = request.seo_title;
    content.seo_description = request.seo_description;
    content.allow_comments = request.allow_comments.unwrap_or(true);
    content.sort_order = request.sort_order.unwrap_or(0);
    content.metadata = request.metadata.unwrap_or_default();

    match service.create_content(&content).await {
        Ok(()) => Json(ApiResponse::success(content)),
        Err(e) => {
            tracing::error!("Failed to create content: {}", e);
            Json(ApiResponse::error("Failed to create content".to_string()))
        }
    }
}

// Note: Update and delete endpoints removed for now to fix compilation
// They can be re-added later with proper implementation

pub async fn search_contents(
    State(service): State<Arc<ContentService>>,
    Query(params): Query<HashMap<String, String>>,
) -> impl IntoResponse {
    let search_term = params.get("q").cloned().unwrap_or_default();
    let limit = params
        .get("limit")
        .and_then(|l| l.parse::<i64>().ok())
        .unwrap_or(20);

    if search_term.is_empty() {
        return Json(ApiResponse::error("Search term is required".to_string()));
    }

    match service.search_contents(&search_term, Some(limit)).await {
        Ok(contents) => {
            let total_count = contents.len() as i64;
            Json(ApiResponse::success(ContentListResponse {
                contents,
                total_count,
                has_more: total_count >= limit,
            }))
        }
        Err(e) => {
            tracing::error!("Failed to search contents: {}", e);
            Json(ApiResponse::error("Failed to search contents".to_string()))
        }
    }
}

pub async fn get_published_contents(
    State(service): State<Arc<ContentService>>,
    Query(params): Query<HashMap<String, String>>,
) -> impl IntoResponse {
    let limit = params
        .get("limit")
        .and_then(|l| l.parse::<i64>().ok())
        .unwrap_or(50);

    match service.get_published_contents(Some(limit)).await {
        Ok(contents) => {
            let total_count = contents.len() as i64;
            Json(ApiResponse::success(ContentListResponse {
                contents,
                total_count,
                has_more: total_count >= limit,
            }))
        }
        Err(e) => {
            tracing::error!("Failed to get published contents: {}", e);
            Json(ApiResponse::error(
                "Failed to retrieve published contents".to_string(),
            ))
        }
    }
}

pub async fn get_content_stats(State(service): State<Arc<ContentService>>) -> impl IntoResponse {
    match service.get_content_stats().await {
        Ok(stats) => Json(ApiResponse::success(stats)),
        Err(e) => {
            tracing::error!("Failed to get content stats: {}", e);
            Json(ApiResponse::error(
                "Failed to retrieve content statistics".to_string(),
            ))
        }
    }
}

pub async fn get_all_tags(State(service): State<Arc<ContentService>>) -> impl IntoResponse {
    match service.get_all_tags().await {
        Ok(tags) => Json(ApiResponse::success(tags)),
        Err(e) => {
            tracing::error!("Failed to get tags: {}", e);
            Json(ApiResponse::error("Failed to retrieve tags".to_string()))
        }
    }
}

pub async fn get_all_categories(State(service): State<Arc<ContentService>>) -> impl IntoResponse {
    match service.get_all_categories().await {
        Ok(categories) => Json(ApiResponse::success(categories)),
        Err(e) => {
            tracing::error!("Failed to get categories: {}", e);
            Json(ApiResponse::error(
                "Failed to retrieve categories".to_string(),
            ))
        }
    }
}

pub async fn get_contents_by_type(
    State(service): State<Arc<ContentService>>,
    Path(content_type): Path<String>,
) -> impl IntoResponse {
    let content_type = ContentType::from(content_type);

    match service.get_contents_by_type(content_type).await {
        Ok(contents) => {
            let total_count = contents.len() as i64;
            Json(ApiResponse::success(ContentListResponse {
                contents,
                total_count,
                has_more: false,
            }))
        }
        Err(e) => {
            tracing::error!("Failed to get contents by type: {}", e);
            Json(ApiResponse::error(
                "Failed to retrieve contents by type".to_string(),
            ))
        }
    }
}

pub async fn get_contents_by_category(
    State(service): State<Arc<ContentService>>,
    Path(category): Path<String>,
) -> impl IntoResponse {
    match service.get_contents_by_category(&category).await {
        Ok(contents) => {
            let total_count = contents.len() as i64;
            Json(ApiResponse::success(ContentListResponse {
                contents,
                total_count,
                has_more: false,
            }))
        }
        Err(e) => {
            tracing::error!("Failed to get contents by category: {}", e);
            Json(ApiResponse::error(
                "Failed to retrieve contents by category".to_string(),
            ))
        }
    }
}

pub async fn get_contents_by_author(
    State(service): State<Arc<ContentService>>,
    Path(author_id): Path<Uuid>,
) -> impl IntoResponse {
    match service.get_contents_by_author(author_id).await {
        Ok(contents) => {
            let total_count = contents.len() as i64;
            Json(ApiResponse::success(ContentListResponse {
                contents,
                total_count,
                has_more: false,
            }))
        }
        Err(e) => {
            tracing::error!("Failed to get contents by author: {}", e);
            Json(ApiResponse::error(
                "Failed to retrieve contents by author".to_string(),
            ))
        }
    }
}

pub async fn get_recent_contents(
    State(service): State<Arc<ContentService>>,
    Query(params): Query<HashMap<String, String>>,
) -> impl IntoResponse {
    let limit = params
        .get("limit")
        .and_then(|l| l.parse::<i64>().ok())
        .unwrap_or(10);

    match service.get_recent_contents(limit).await {
        Ok(contents) => {
            let total_count = contents.len() as i64;
            Json(ApiResponse::success(ContentListResponse {
                contents,
                total_count,
                has_more: total_count >= limit,
            }))
        }
        Err(e) => {
            tracing::error!("Failed to get recent contents: {}", e);
            Json(ApiResponse::error(
                "Failed to retrieve recent contents".to_string(),
            ))
        }
    }
}

pub async fn get_popular_contents(
    State(service): State<Arc<ContentService>>,
    Query(params): Query<HashMap<String, String>>,
) -> impl IntoResponse {
    let limit = params
        .get("limit")
        .and_then(|l| l.parse::<i64>().ok())
        .unwrap_or(10);

    match service.get_popular_contents(limit).await {
        Ok(contents) => {
            let total_count = contents.len() as i64;
            Json(ApiResponse::success(ContentListResponse {
                contents,
                total_count,
                has_more: total_count >= limit,
            }))
        }
        Err(e) => {
            tracing::error!("Failed to get popular contents: {}", e);
            Json(ApiResponse::error(
                "Failed to retrieve popular contents".to_string(),
            ))
        }
    }
}

pub async fn increment_view_count(
    State(service): State<Arc<ContentService>>,
    Path(id): Path<Uuid>,
) -> impl IntoResponse {
    match service.increment_view_count(id).await {
        Ok(()) => Json(ApiResponse::success("View count incremented")),
        Err(e) => {
            tracing::error!("Failed to increment view count: {}", e);
            Json(ApiResponse::error(
                "Failed to increment view count".to_string(),
            ))
        }
    }
}

pub async fn get_table_of_contents(
    State(service): State<Arc<ContentService>>,
    Path(id): Path<Uuid>,
) -> impl IntoResponse {
    match service.get_content_by_id(id).await {
        Ok(Some(content)) => {
            let renderer = ContentRenderer::new();
            match renderer.generate_table_of_contents(&content) {
                Ok(toc) => Json(ApiResponse::success(toc)),
                Err(e) => {
                    tracing::error!("Failed to generate table of contents: {}", e);
                    Json(ApiResponse::error(
                        "Failed to generate table of contents".to_string(),
                    ))
                }
            }
        }
        Ok(None) => Json(ApiResponse::error("Content not found".to_string())),
        Err(e) => {
            tracing::error!("Failed to get content for TOC: {}", e);
            Json(ApiResponse::error("Failed to retrieve content".to_string()))
        }
    }
}

pub async fn reload_content(State(service): State<Arc<ContentService>>) -> impl IntoResponse {
    match service.reload_file_content().await {
        Ok(()) => Json(ApiResponse::success("Content reloaded successfully")),
        Err(e) => {
            tracing::error!("Failed to reload content: {}", e);
            Json(ApiResponse::error("Failed to reload content".to_string()))
        }
    }
}

pub async fn publish_scheduled_content(
    State(service): State<Arc<ContentService>>,
) -> impl IntoResponse {
    match service.publish_scheduled_content().await {
        Ok(published_contents) => {
            let count = published_contents.len();
            Json(ApiResponse::success(format!(
                "Published {} scheduled contents",
                count
            )))
        }
        Err(e) => {
            tracing::error!("Failed to publish scheduled content: {}", e);
            Json(ApiResponse::error(
                "Failed to publish scheduled content".to_string(),
            ))
        }
    }
}

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

    #[test]
    fn test_content_query_params_conversion() {
        let params = ContentQueryParams {
            content_type: Some("blog".to_string()),
            state: Some("published".to_string()),
            author_id: None,
            category: Some("tech".to_string()),
            tags: Some("rust,web".to_string()),
            require_login: Some(false),
            search: Some("test".to_string()),
            limit: Some(10),
            offset: Some(0),
            sort_by: Some("created_at".to_string()),
            sort_order: Some("DESC".to_string()),
        };

        let query = ContentQuery::from(params);
        assert_eq!(query.content_type, Some(ContentType::Blog));
        assert_eq!(query.state, Some(ContentState::Published));
        assert_eq!(query.category, Some("tech".to_string()));
        assert_eq!(
            query.tags,
            Some(vec!["rust".to_string(), "web".to_string()])
        );
        assert_eq!(query.require_login, Some(false));
        assert_eq!(query.search, Some("test".to_string()));
        assert_eq!(query.limit, Some(10));
        assert_eq!(query.offset, Some(0));
        assert_eq!(query.sort_by, Some("created_at".to_string()));
        assert_eq!(query.sort_order, Some("DESC".to_string()));
    }

    #[test]
    fn test_api_response_creation() {
        let success_response = ApiResponse::success("test data");
        assert!(success_response.success);
        assert_eq!(success_response.data, Some("test data"));
        assert!(success_response.message.is_none());
        assert!(success_response.errors.is_none());

        let error_response: ApiResponse<()> = ApiResponse::error("test error".to_string());
        assert!(!error_response.success);
        assert!(error_response.data.is_none());
        assert_eq!(error_response.message, Some("test error".to_string()));
        assert!(error_response.errors.is_none());

        let validation_response: ApiResponse<()> =
            ApiResponse::validation_error(vec!["error1".to_string(), "error2".to_string()]);
        assert!(!validation_response.success);
        assert!(validation_response.data.is_none());
        assert_eq!(
            validation_response.message,
            Some("Validation failed".to_string())
        );
        assert_eq!(
            validation_response.errors,
            Some(vec!["error1".to_string(), "error2".to_string()])
        );
    }
}
