from datetime import datetime, timezone
from uuid import UUID

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

from src.apps.memorials.models.memorial import Memorial
from src.apps.memorials.models.tribute import Tribute
from src.apps.memorials.schemas.requests import UpdateMemorialRequest, UpdateTributeStatusRequest
from src.apps.memorials.schemas.responses import MemorialResponse, MemorialSummaryResponse, TributeResponse
from src.core.constants import UserRole
from src.core.dependencies import get_current_user, require_roles, require_tenant
from src.core.exceptions import NotFoundError
from src.core.schemas.response import paginated, success
from src.database.session import get_db

router = APIRouter(prefix="/memorials", tags=["Memorials"])


@router.get("", response_model=dict)
async def list_memorials(
    skip: int = Query(0, ge=0),
    limit: int = Query(20, ge=1, le=100),
    current_user=Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    count_result = await db.execute(
        select(func.count(Memorial.id)).where(Memorial.tenant_id == current_user.tenant_id)
    )
    total = count_result.scalar_one()

    result = await db.execute(
        select(Memorial)
        .where(Memorial.tenant_id == current_user.tenant_id)
        .order_by(Memorial.created_at.desc())
        .offset(skip)
        .limit(limit)
    )
    memorials = result.scalars().all()
    return paginated(
        items=[MemorialSummaryResponse.model_validate(m).model_dump() for m in memorials],
        total=total,
        page=(skip // limit) + 1,
        page_size=limit,
    )


@router.get("/tributes", response_model=dict)
async def list_pending_tributes(
    skip: int = Query(0, ge=0),
    limit: int = Query(20, ge=1, le=100),
    current_user=Depends(
        require_roles(UserRole.ADMINISTRATOR, UserRole.MANAGER, UserRole.STAFF)
    ),
    db: AsyncSession = Depends(get_db),
):
    """List tributes pending moderation."""
    conditions = [
        Tribute.tenant_id == current_user.tenant_id,
        Tribute.status == "pending",
    ]

    count_result = await db.execute(
        select(func.count(Tribute.id)).where(and_(*conditions))
    )
    total = count_result.scalar_one()

    result = await db.execute(
        select(Tribute)
        .where(and_(*conditions))
        .order_by(Tribute.submitted_at.asc())
        .offset(skip)
        .limit(limit)
    )
    tributes = result.scalars().all()
    return paginated(
        items=[TributeResponse.model_validate(t).model_dump() for t in tributes],
        total=total,
        page=(skip // limit) + 1,
        page_size=limit,
    )


@router.get("/{memorial_id}", response_model=dict)
async def get_memorial(
    memorial_id: UUID,
    current_user=Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    result = await db.execute(
        select(Memorial)
        .options(selectinload(Memorial.tributes))
        .where(
            and_(
                Memorial.id == memorial_id,
                Memorial.tenant_id == current_user.tenant_id,
            )
        )
    )
    memorial = result.scalar_one_or_none()
    if not memorial:
        raise NotFoundError("Memorial not found")
    return success(data=MemorialResponse.model_validate(memorial).model_dump())


@router.patch("/{memorial_id}", response_model=dict)
async def update_memorial(
    memorial_id: UUID,
    body: UpdateMemorialRequest,
    current_user=Depends(
        require_roles(UserRole.ADMINISTRATOR, UserRole.MANAGER, UserRole.STAFF)
    ),
    db: AsyncSession = Depends(get_db),
):
    result = await db.execute(
        select(Memorial).where(
            and_(
                Memorial.id == memorial_id,
                Memorial.tenant_id == current_user.tenant_id,
            )
        )
    )
    memorial = result.scalar_one_or_none()
    if not memorial:
        raise NotFoundError("Memorial not found")

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

    await db.flush()
    await db.refresh(memorial)
    return success(
        data=MemorialSummaryResponse.model_validate(memorial).model_dump(),
        message="Memorial updated",
    )


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

    memorial.is_published = not memorial.is_published
    memorial.published_at = datetime.now(timezone.utc) if memorial.is_published else None

    await db.flush()
    await db.refresh(memorial)
    action = "published" if memorial.is_published else "unpublished"
    return success(
        data=MemorialSummaryResponse.model_validate(memorial).model_dump(),
        message=f"Memorial {action}",
    )


@router.patch("/tributes/{tribute_id}/status", response_model=dict)
async def update_tribute_status(
    tribute_id: UUID,
    body: UpdateTributeStatusRequest,
    current_user=Depends(
        require_roles(UserRole.ADMINISTRATOR, UserRole.MANAGER, UserRole.STAFF)
    ),
    db: AsyncSession = Depends(get_db),
):
    result = await db.execute(
        select(Tribute).where(
            and_(
                Tribute.id == tribute_id,
                Tribute.tenant_id == current_user.tenant_id,
            )
        )
    )
    tribute = result.scalar_one_or_none()
    if not tribute:
        raise NotFoundError("Tribute not found")

    tribute.status = body.status
    tribute.moderated_by = current_user.id
    tribute.moderated_at = datetime.now(timezone.utc)

    await db.flush()
    await db.refresh(tribute)
    return success(
        data=TributeResponse.model_validate(tribute).model_dump(),
        message=f"Tribute {body.status}",
    )
