from datetime import datetime, timezone
from uuid import UUID

from fastapi import APIRouter, Depends, Query
from sqlalchemy import and_, func, select
from sqlalchemy.ext.asyncio import AsyncSession

from src.apps.news.models.news_post import NewsPost
from src.apps.news.schemas.requests import CreateNewsRequest, UpdateNewsRequest
from src.apps.news.schemas.responses import NewsPostResponse
from src.core.constants import UserRole
from src.core.dependencies import get_current_user, require_roles
from src.core.exceptions import NotFoundError
from src.core.schemas.response import paginated, success
from src.database.session import get_db

router = APIRouter(prefix="/news", tags=["News"])


@router.get("", response_model=dict)
async def list_news(
    skip: int = Query(0, ge=0),
    limit: int = Query(20, ge=1, le=100),
    status: str = Query(None),
    current_user=Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    conditions = [NewsPost.tenant_id == current_user.tenant_id]
    if status:
        conditions.append(NewsPost.status == status)

    where_clause = and_(*conditions)

    count_result = await db.execute(
        select(func.count(NewsPost.id)).where(where_clause)
    )
    total = count_result.scalar_one()

    result = await db.execute(
        select(NewsPost)
        .where(where_clause)
        .order_by(NewsPost.created_at.desc())
        .offset(skip)
        .limit(limit)
    )
    posts = result.scalars().all()
    return paginated(
        items=[NewsPostResponse.model_validate(p).model_dump() for p in posts],
        total=total,
        page=(skip // limit) + 1,
        page_size=limit,
    )


@router.post("", response_model=dict, status_code=201)
async def create_news(
    body: CreateNewsRequest,
    current_user=Depends(
        require_roles(UserRole.ADMINISTRATOR, UserRole.MANAGER, UserRole.STAFF)
    ),
    db: AsyncSession = Depends(get_db),
):
    post = NewsPost(
        tenant_id=current_user.tenant_id,
        author_id=current_user.id,
        **body.model_dump(exclude_none=True),
    )
    if post.status == "published" and not post.published_at:
        post.published_at = datetime.now(timezone.utc)

    db.add(post)
    await db.flush()
    await db.refresh(post)
    return success(
        data=NewsPostResponse.model_validate(post).model_dump(),
        message="News post created",
    )


@router.get("/{post_id}", response_model=dict)
async def get_news(
    post_id: UUID,
    current_user=Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    result = await db.execute(
        select(NewsPost).where(
            and_(
                NewsPost.id == post_id,
                NewsPost.tenant_id == current_user.tenant_id,
            )
        )
    )
    post = result.scalar_one_or_none()
    if not post:
        raise NotFoundError("News post not found")
    return success(data=NewsPostResponse.model_validate(post).model_dump())


@router.put("/{post_id}", response_model=dict)
async def update_news(
    post_id: UUID,
    body: UpdateNewsRequest,
    current_user=Depends(
        require_roles(UserRole.ADMINISTRATOR, UserRole.MANAGER, UserRole.STAFF)
    ),
    db: AsyncSession = Depends(get_db),
):
    result = await db.execute(
        select(NewsPost).where(
            and_(
                NewsPost.id == post_id,
                NewsPost.tenant_id == current_user.tenant_id,
            )
        )
    )
    post = result.scalar_one_or_none()
    if not post:
        raise NotFoundError("News post not found")

    update_data = body.model_dump(exclude_none=True)
    for field, value in update_data.items():
        if hasattr(post, field):
            setattr(post, field, value)

    await db.flush()
    await db.refresh(post)
    return success(
        data=NewsPostResponse.model_validate(post).model_dump(),
        message="News post updated",
    )


@router.delete("/{post_id}", status_code=204)
async def delete_news(
    post_id: UUID,
    current_user=Depends(require_roles(UserRole.ADMINISTRATOR, UserRole.MANAGER)),
    db: AsyncSession = Depends(get_db),
):
    result = await db.execute(
        select(NewsPost).where(
            and_(
                NewsPost.id == post_id,
                NewsPost.tenant_id == current_user.tenant_id,
            )
        )
    )
    post = result.scalar_one_or_none()
    if not post:
        raise NotFoundError("News post not found")
    await db.delete(post)
    await db.flush()


@router.patch("/{post_id}/publish", response_model=dict)
async def toggle_publish(
    post_id: UUID,
    current_user=Depends(
        require_roles(UserRole.ADMINISTRATOR, UserRole.MANAGER, UserRole.STAFF)
    ),
    db: AsyncSession = Depends(get_db),
):
    result = await db.execute(
        select(NewsPost).where(
            and_(
                NewsPost.id == post_id,
                NewsPost.tenant_id == current_user.tenant_id,
            )
        )
    )
    post = result.scalar_one_or_none()
    if not post:
        raise NotFoundError("News post not found")

    if post.status == "published":
        post.status = "draft"
        post.published_at = None
    else:
        post.status = "published"
        post.published_at = datetime.now(timezone.utc)

    await db.flush()
    await db.refresh(post)
    action = "published" if post.status == "published" else "unpublished"
    return success(
        data=NewsPostResponse.model_validate(post).model_dump(),
        message=f"News post {action}",
    )
