"""
Blog API endpoints.

Public endpoints for reading blog posts.
Admin endpoints for managing blog posts.
"""
import re
from datetime import datetime
from typing import Optional
from fastapi import APIRouter, HTTPException, status, Depends
from pydantic import BaseModel
from sqlalchemy import select, func, desc
from sqlalchemy.orm import selectinload

from app.api.deps import Database, get_current_user, get_current_user_optional
from app.models.user import User
from app.models.blog import BlogPost
from app.services.html_sanitizer import sanitize_html

router = APIRouter()


# ============== Schemas ==============

class BlogPostCreate(BaseModel):
    """Schema for creating a blog post."""
    title: str
    content: str
    excerpt: Optional[str] = None
    cover_image: Optional[str] = None
    category: Optional[str] = None
    tags: Optional[list[str]] = None
    meta_title: Optional[str] = None
    meta_description: Optional[str] = None
    is_published: bool = False


class BlogPostUpdate(BaseModel):
    """Schema for updating a blog post."""
    title: Optional[str] = None
    content: Optional[str] = None
    excerpt: Optional[str] = None
    cover_image: Optional[str] = None
    category: Optional[str] = None
    tags: Optional[list[str]] = None
    meta_title: Optional[str] = None
    meta_description: Optional[str] = None
    is_published: Optional[bool] = None


# ============== Helper Functions ==============

def generate_slug(title: str) -> str:
    """Generate URL-friendly slug from title."""
    # Convert to lowercase
    slug = title.lower()
    # Replace spaces with hyphens
    slug = re.sub(r'\s+', '-', slug)
    # Remove special characters
    slug = re.sub(r'[^a-z0-9\-]', '', slug)
    # Remove multiple hyphens
    slug = re.sub(r'-+', '-', slug)
    # Remove leading/trailing hyphens
    slug = slug.strip('-')
    return slug


async def require_admin(
    current_user: User = Depends(get_current_user),
) -> User:
    """Dependency that requires admin privileges."""
    if not current_user.is_admin:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Admin privileges required"
        )
    return current_user


# ============== Public Endpoints ==============

@router.get("/posts")
async def list_blog_posts(
    db: Database,
    limit: int = 10,
    offset: int = 0,
    category: Optional[str] = None,
    tag: Optional[str] = None,
):
    """
    List published blog posts.
    
    Returns paginated list of published posts with metadata.
    """
    query = (
        select(BlogPost)
        .options(selectinload(BlogPost.author))
        .where(BlogPost.is_published == True)
        .order_by(desc(BlogPost.published_at))
    )
    
    if category:
        query = query.where(BlogPost.category == category)
    
    if tag:
        query = query.where(BlogPost.tags.ilike(f"%{tag}%"))
    
    query = query.offset(offset).limit(limit)
    result = await db.execute(query)
    posts = result.scalars().all()
    
    # Total count
    count_query = select(func.count(BlogPost.id)).where(BlogPost.is_published == True)
    if category:
        count_query = count_query.where(BlogPost.category == category)
    if tag:
        count_query = count_query.where(BlogPost.tags.ilike(f"%{tag}%"))
    total = await db.execute(count_query)
    total = total.scalar()
    
    return {
        "posts": [post.to_dict(include_content=False) for post in posts],
        "total": total,
        "limit": limit,
        "offset": offset,
    }


@router.get("/posts/featured")
async def get_featured_posts(
    db: Database,
    limit: int = 3,
):
    """Get featured/latest blog posts for homepage."""
    query = (
        select(BlogPost)
        .options(selectinload(BlogPost.author))
        .where(BlogPost.is_published == True)
        .order_by(desc(BlogPost.published_at))
        .limit(limit)
    )
    result = await db.execute(query)
    posts = result.scalars().all()
    
    return {
        "posts": [post.to_dict(include_content=False) for post in posts]
    }


@router.get("/posts/categories")
async def get_categories(db: Database):
    """Get all blog categories with post counts."""
    result = await db.execute(
        select(BlogPost.category, func.count(BlogPost.id))
        .where(BlogPost.is_published == True, BlogPost.category.isnot(None))
        .group_by(BlogPost.category)
    )
    categories = result.all()
    
    return {
        "categories": [
            {"name": cat, "count": count}
            for cat, count in categories
        ]
    }


@router.get("/posts/{slug}")
async def get_blog_post(
    slug: str,
    db: Database,
):
    """
    Get a single blog post by slug.
    
    Increments view count.
    """
    result = await db.execute(
        select(BlogPost)
        .options(selectinload(BlogPost.author))
        .where(
            BlogPost.slug == slug,
            BlogPost.is_published == True
        )
    )
    post = result.scalar_one_or_none()
    
    if not post:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Blog post not found"
        )
    
    # Increment view count
    post.view_count += 1
    await db.commit()
    
    data = post.to_dict(include_content=True)
    data["content"] = sanitize_html(data.get("content") or "")
    return data


@router.get("/posts/{slug}/meta")
async def get_blog_post_meta(
    slug: str,
    db: Database,
):
    """
    Get blog post metadata by slug (public).

    IMPORTANT: This endpoint does NOT increment view_count.
    It's intended for SEO metadata generation (generateMetadata, JSON-LD).
    """
    result = await db.execute(
        select(BlogPost)
        .options(selectinload(BlogPost.author))
        .where(
            BlogPost.slug == slug,
            BlogPost.is_published == True,
        )
    )
    post = result.scalar_one_or_none()

    if not post:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Blog post not found",
        )

    return post.to_dict(include_content=False)


# ============== Admin Endpoints ==============

@router.get("/admin/posts")
async def admin_list_posts(
    db: Database,
    admin: User = Depends(require_admin),
    limit: int = 50,
    offset: int = 0,
    status_filter: Optional[str] = None,  # "published", "draft"
):
    """Admin: List all blog posts (including drafts)."""
    query = select(BlogPost).options(selectinload(BlogPost.author)).order_by(desc(BlogPost.created_at))
    
    if status_filter == "published":
        query = query.where(BlogPost.is_published == True)
    elif status_filter == "draft":
        query = query.where(BlogPost.is_published == False)
    
    query = query.offset(offset).limit(limit)
    result = await db.execute(query)
    posts = result.scalars().all()
    
    # Total count
    count_query = select(func.count(BlogPost.id))
    if status_filter == "published":
        count_query = count_query.where(BlogPost.is_published == True)
    elif status_filter == "draft":
        count_query = count_query.where(BlogPost.is_published == False)
    total = await db.execute(count_query)
    total = total.scalar()
    
    return {
        "posts": [post.to_dict(include_content=False) for post in posts],
        "total": total,
    }


@router.post("/admin/posts")
async def create_blog_post(
    data: BlogPostCreate,
    db: Database,
    admin: User = Depends(require_admin),
):
    """Admin: Create a new blog post."""
    # Generate slug
    slug = generate_slug(data.title)
    
    # Check if slug exists
    existing = await db.execute(
        select(BlogPost).where(BlogPost.slug == slug)
    )
    if existing.scalar_one_or_none():
        # Add timestamp to make unique
        slug = f"{slug}-{int(datetime.utcnow().timestamp())}"
    
    post = BlogPost(
        title=data.title,
        slug=slug,
        content=sanitize_html(data.content),
        excerpt=data.excerpt,
        cover_image=data.cover_image,
        category=data.category,
        tags=",".join(data.tags) if data.tags else None,
        meta_title=data.meta_title,
        meta_description=data.meta_description,
        is_published=data.is_published,
        published_at=datetime.utcnow() if data.is_published else None,
        author_id=admin.id,
    )
    
    db.add(post)
    await db.commit()
    await db.refresh(post)
    
    return post.to_dict()


@router.get("/admin/posts/{post_id}")
async def admin_get_post(
    post_id: int,
    db: Database,
    admin: User = Depends(require_admin),
):
    """Admin: Get a single post (including drafts)."""
    result = await db.execute(
        select(BlogPost)
        .options(selectinload(BlogPost.author))
        .where(BlogPost.id == post_id)
    )
    post = result.scalar_one_or_none()
    
    if not post:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Blog post not found"
        )
    
    return post.to_dict()


@router.patch("/admin/posts/{post_id}")
async def update_blog_post(
    post_id: int,
    data: BlogPostUpdate,
    db: Database,
    admin: User = Depends(require_admin),
):
    """Admin: Update a blog post."""
    result = await db.execute(
        select(BlogPost).where(BlogPost.id == post_id)
    )
    post = result.scalar_one_or_none()
    
    if not post:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Blog post not found"
        )
    
    # Update fields
    if data.title is not None:
        post.title = data.title
        # Optionally update slug if title changes
        # post.slug = generate_slug(data.title)
    if data.content is not None:
        post.content = sanitize_html(data.content)
    if data.excerpt is not None:
        post.excerpt = data.excerpt
    if data.cover_image is not None:
        post.cover_image = data.cover_image
    if data.category is not None:
        post.category = data.category
    if data.tags is not None:
        post.tags = ",".join(data.tags)
    if data.meta_title is not None:
        post.meta_title = data.meta_title
    if data.meta_description is not None:
        post.meta_description = data.meta_description
    if data.is_published is not None:
        was_published = post.is_published
        post.is_published = data.is_published
        # Set published_at when first published
        if data.is_published and not was_published:
            post.published_at = datetime.utcnow()
    
    await db.commit()
    await db.refresh(post)
    
    return post.to_dict()


@router.delete("/admin/posts/{post_id}")
async def delete_blog_post(
    post_id: int,
    db: Database,
    admin: User = Depends(require_admin),
):
    """Admin: Delete a blog post."""
    result = await db.execute(
        select(BlogPost).where(BlogPost.id == post_id)
    )
    post = result.scalar_one_or_none()
    
    if not post:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Blog post not found"
        )
    
    await db.delete(post)
    await db.commit()
    
    return {"message": "Blog post deleted"}


@router.post("/admin/posts/{post_id}/publish")
async def publish_blog_post(
    post_id: int,
    db: Database,
    admin: User = Depends(require_admin),
):
    """Admin: Publish a draft post."""
    result = await db.execute(
        select(BlogPost).where(BlogPost.id == post_id)
    )
    post = result.scalar_one_or_none()
    
    if not post:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Blog post not found"
        )
    
    post.is_published = True
    post.published_at = datetime.utcnow()
    await db.commit()
    
    return {"message": "Blog post published", "published_at": post.published_at.isoformat()}


@router.post("/admin/posts/{post_id}/unpublish")
async def unpublish_blog_post(
    post_id: int,
    db: Database,
    admin: User = Depends(require_admin),
):
    """Admin: Unpublish a post (make it a draft)."""
    result = await db.execute(
        select(BlogPost).where(BlogPost.id == post_id)
    )
    post = result.scalar_one_or_none()
    
    if not post:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Blog post not found"
        )
    
    post.is_published = False
    await db.commit()
    
    return {"message": "Blog post unpublished"}

