from datetime import datetime, timezone
from typing import Optional
from uuid import UUID

from fastapi import APIRouter, Depends, Query, Request
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, ReplaceTimelineRequest
from src.apps.memorials.schemas.responses import MemorialResponse, MemorialSummaryResponse, TributeResponse
from src.apps.site_admin.services.audit_service import AuditService
from src.core.constants import UserRole
from src.core.dependencies import get_current_user, require_roles, require_tenant, require_min_role
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_tributes(
    skip: int = Query(0, ge=0),
    limit: int = Query(20, ge=1, le=100),
    status: Optional[str] = Query(None, description="Filter by status: pending | approved | rejected"),
    memorial_id: Optional[UUID] = Query(None, description="Filter to a specific memorial"),
    current_user=Depends(
        require_roles(UserRole.ADMINISTRATOR, UserRole.MANAGER, UserRole.STAFF)
    ),
    db: AsyncSession = Depends(get_db),
):
    """List tributes with optional filtering by status and memorial."""
    conditions = [
        Tribute.tenant_id == current_user.tenant_id,
    ]
    if status:
        conditions.append(Tribute.status == status)
    elif memorial_id is None:
        # When querying the global moderation queue (no specific memorial),
        # default to pending-only to avoid returning all tributes across the tenant.
        conditions.append(Tribute.status == "pending")
    # When memorial_id is supplied without a status filter, return all statuses for that memorial.

    if memorial_id:
        conditions.append(Tribute.memorial_id == memorial_id)

    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),
):
    from src.apps.memorials.models.timeline_event import MemorialTimelineEvent
    result = await db.execute(
        select(Memorial)
        .options(
            selectinload(Memorial.tributes),
            selectinload(Memorial.timeline_events),
        )
        .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,
    request: Request,
    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")

    old_values = {
        "biography_text": memorial.biography_text,
        "is_published": memorial.is_published,
        "display_name": memorial.display_name,
        "headstone_inscription": memorial.headstone_inscription,
    }

    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)

    await AuditService.log(
        db,
        "memorial",
        memorial.id,
        "updated",
        current_user,
        request,
        old_value=old_values,
        new_value=update_data,
    )

    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,
    request: Request,
    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")

    old_published = memorial.is_published
    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"
    await AuditService.log(
        db,
        "memorial",
        memorial.id,
        action,
        current_user,
        request,
        old_value={"is_published": old_published},
        new_value={"is_published": memorial.is_published},
    )

    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,
    request: Request,
    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")

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

    await db.flush()
    await db.refresh(tribute)

    await AuditService.log(
        db,
        "tribute",
        tribute.id,
        f"status_{body.status}",
        current_user,
        request,
        old_value={"status": old_status},
        new_value={"status": tribute.status},
    )

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


@router.post("/{memorial_id}/biography", response_model=dict)
async def generate_biography_for_memorial(
    memorial_id: UUID,
    current_user=Depends(
        require_roles(UserRole.ADMINISTRATOR, UserRole.MANAGER, UserRole.STAFF)
    ),
    db: AsyncSession = Depends(get_db),
):
    """
    Generate an AI biography draft for a memorial, using data from the linked
    Record. Stores the result in memorial.ai_biography_text (separate from the
    user-editable biography_text column). Returns the generated text.
    """
    from fastapi import HTTPException, status as http_status
    from src.core.config import settings
    from src.apps.records.models.record import Record

    # 1. Load the memorial (tenant-scoped)
    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")

    # 2. Load the linked record
    record_result = await db.execute(
        select(Record).where(
            Record.id == memorial.record_id,
            Record.tenant_id == current_user.tenant_id,
            Record.deleted_at.is_(None),
        )
    )
    record = record_result.scalar_one_or_none()
    if not record:
        raise NotFoundError("Associated record not found")

    if not settings.ANTHROPIC_API_KEY:
        raise HTTPException(
            status_code=http_status.HTTP_503_SERVICE_UNAVAILABLE,
            detail="AI service is not configured. Set ANTHROPIC_API_KEY.",
        )

    # 3. Build the prompt from record data
    life_span = ""
    if record.date_of_birth and record.date_of_death:
        life_span = f" ({record.date_of_birth.year}–{record.date_of_death.year})"
    elif record.date_of_birth:
        life_span = f" (born {record.date_of_birth.year})"

    display_name = memorial.display_name or f"{record.first_name} {record.last_name}"
    prompt = (
        f"You are a compassionate writer helping a cemetery staff member create a "
        f"memorial biography. Write a biography for {display_name}{life_span} "
        f"in a warm and respectful tone.\n\n"
        f"Write 2–4 paragraphs. Focus on celebrating their life. "
        f"Do not add unverified facts. Keep it dignified and heartfelt."
    )

    # 4. Call Claude
    try:
        import anthropic
        client = anthropic.AsyncAnthropic(api_key=settings.ANTHROPIC_API_KEY)
        message = await client.messages.create(
            model="claude-opus-4-5",
            max_tokens=1024,
            messages=[{"role": "user", "content": prompt}],
        )
        biography_text = message.content[0].text.strip()
    except Exception as exc:  # pragma: no cover
        raise HTTPException(
            status_code=http_status.HTTP_502_BAD_GATEWAY,
            detail=f"AI service error: {str(exc)}",
        )

    # 5. Persist the AI draft in ai_biography_text (does NOT touch biography_text)
    memorial.ai_biography_text = biography_text
    await db.flush()
    await db.refresh(memorial)

    return success(
        data={"ai_biography_text": biography_text},
        message="AI biography draft generated",
    )


@router.get("/{memorial_id}/timeline", response_model=dict)
async def get_timeline(
    memorial_id: UUID,
    current_user=Depends(require_min_role(UserRole.STAFF)),
    db: AsyncSession = Depends(get_db),
):
    # Verify memorial belongs to tenant
    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")

    # Load timeline events ordered by sort_order
    from src.apps.memorials.models.timeline_event import MemorialTimelineEvent
    from src.apps.memorials.schemas.responses import TimelineEventResponse
    result = await db.execute(
        select(MemorialTimelineEvent)
        .where(
            and_(
                MemorialTimelineEvent.memorial_id == memorial_id,
                MemorialTimelineEvent.tenant_id == current_user.tenant_id,
            )
        )
        .order_by(MemorialTimelineEvent.sort_order)
    )
    events = result.scalars().all()
    return success(
        data=[TimelineEventResponse.model_validate(e).model_dump() for e in events]
    )


@router.put("/{memorial_id}/timeline", response_model=dict)
async def replace_timeline(
    memorial_id: UUID,
    body: ReplaceTimelineRequest,
    request: Request,
    current_user=Depends(require_min_role(UserRole.STAFF)),
    db: AsyncSession = Depends(get_db),
):
    from src.apps.memorials.services.memorial_service import MemorialService
    from src.apps.memorials.schemas.responses import TimelineEventResponse
    events = await MemorialService.replace_timeline(
        db=db,
        memorial_id=memorial_id,
        tenant_id=current_user.tenant_id,
        events=body.events,
        current_user=current_user,
        request=request,
    )
    return success(
        data=[TimelineEventResponse.model_validate(e).model_dump() for e in events],
        message="Timeline updated",
    )
