"""REST API routes for calendar operations (U2).

Thin wrapper over CalendarService. All business logic lives in the
service layer — routes handle HTTP concerns (auth, request validation).

Endpoints (all under /api/v1/calendar):
- POST   /calendar/events — create event
- GET    /calendar/events — list with filters (start, end, type_id, tag_id)
- GET    /calendar/events/{event_id} — get single event
- PATCH  /calendar/events/{event_id} — update event
- DELETE /calendar/events/{event_id} — delete event
- POST   /calendar/events/{event_id}/invitations — invite user by email
- POST   /calendar/invitations/{invitation_id}/respond — accept/decline/tentative
- GET    /calendar/invitations — list invitations for current user
- GET    /calendar/users/search?q=xxx — search users (G5/A3)
- GET    /calendar/event-types — list event types
- POST   /calendar/event-types — create event type
- PATCH  /calendar/event-types/{type_id} — update event type
- GET    /calendar/tags — list tags
- POST   /calendar/tags — create tag
"""

from __future__ import annotations

import logging
from typing import Any

from fastapi import APIRouter, Depends, File, HTTPException, Query, Request, UploadFile
from fastapi.responses import Response
from pydantic import BaseModel, Field

from agentkit.server.auth.dependencies import require_authenticated

logger = logging.getLogger(__name__)

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

_VALID_INVITATION_STATUSES = {"accepted", "declined", "tentative"}


# ---------------------------------------------------------------------------
# Service accessor
# ---------------------------------------------------------------------------


def _get_calendar_service(request: Request):
    """Get CalendarService from app.state. Raises 503 if not initialized."""
    service = getattr(request.app.state, "calendar_service", None)
    if service is None:
        raise HTTPException(
            status_code=503,
            detail="Calendar service not available. Server may not have initialized it.",
        )
    return service


# ---------------------------------------------------------------------------
# Request / response models
# ---------------------------------------------------------------------------


class CreateEventRequest(BaseModel):
    title: str
    start_time: str
    end_time: str
    description: str = ""
    location: str = ""
    is_all_day: bool = False
    event_type_id: str | None = None
    rrule: str | None = None
    tag_ids: list[str] = Field(default_factory=list)


class UpdateEventRequest(BaseModel):
    title: str | None = None
    start_time: str | None = None
    end_time: str | None = None
    description: str | None = None
    location: str | None = None
    is_all_day: bool | None = None
    event_type_id: str | None = None
    rrule: str | None = None


class CreateEventTypeRequest(BaseModel):
    name: str
    color: str = "#4A90D9"


class UpdateEventTypeRequest(BaseModel):
    name: str | None = None
    color: str | None = None
    is_default: bool | None = None


class CreateTagRequest(BaseModel):
    name: str


class CreateInvitationRequest(BaseModel):
    invitee_email: str


class RespondInvitationRequest(BaseModel):
    status: str


# ---------------------------------------------------------------------------
# Event endpoints
# ---------------------------------------------------------------------------


@router.post("/events")
async def create_event(
    body: CreateEventRequest,
    request: Request,
    user: dict = Depends(require_authenticated),
) -> dict[str, Any]:
    """Create a new calendar event."""
    service = _get_calendar_service(request)
    event = await service.create_event(
        user_id=user["user_id"],
        title=body.title,
        start_time=body.start_time,
        end_time=body.end_time,
        description=body.description,
        location=body.location,
        is_all_day=body.is_all_day,
        event_type_id=body.event_type_id,
        rrule=body.rrule,
        source="manual",
        tag_ids=body.tag_ids,
    )
    return {"success": True, "event": event.to_dict()}


@router.get("/events")
async def list_events(
    request: Request,
    start: str | None = Query(None),
    end: str | None = Query(None),
    type_id: str | None = Query(None),
    tag_id: str | None = Query(None),
    user: dict = Depends(require_authenticated),
) -> dict[str, Any]:
    """List events for the current user with optional filters."""
    service = _get_calendar_service(request)
    events = await service.list_events(
        user_id=user["user_id"],
        start=start,
        end=end,
        event_type_id=type_id,
        tag_id=tag_id,
    )
    return {
        "success": True,
        "events": [e.to_dict() for e in events],
        "count": len(events),
    }


@router.get("/events/{event_id}")
async def get_event(
    event_id: str,
    request: Request,
    user: dict = Depends(require_authenticated),
) -> dict[str, Any]:
    """Get a single event by id."""
    service = _get_calendar_service(request)
    event = await service.get_event(event_id)
    if event is None:
        raise HTTPException(status_code=404, detail="Event not found")
    if event.user_id != user["user_id"]:
        raise HTTPException(status_code=403, detail="Access denied")
    return {"success": True, "event": event.to_dict()}


@router.patch("/events/{event_id}")
async def update_event(
    event_id: str,
    body: UpdateEventRequest,
    request: Request,
    user: dict = Depends(require_authenticated),
) -> dict[str, Any]:
    """Update specific fields of an event."""
    service = _get_calendar_service(request)
    event = await service.get_event(event_id)
    if event is None:
        raise HTTPException(status_code=404, detail="Event not found")
    if event.user_id != user["user_id"]:
        raise HTTPException(status_code=403, detail="Access denied")

    # Build fields dict from non-None values
    fields: dict[str, Any] = {
        name: value
        for name, value in body.model_dump(exclude_unset=True).items()
        if value is not None
    }

    if not fields:
        return {"success": True, "event": event.to_dict(), "updated": False}

    updated = await service.update_event(event_id, fields)
    refreshed = await service.get_event(event_id)
    return {
        "success": True,
        "event": refreshed.to_dict() if refreshed else event.to_dict(),
        "updated": updated,
    }


@router.delete("/events/{event_id}")
async def delete_event(
    event_id: str,
    request: Request,
    user: dict = Depends(require_authenticated),
) -> dict[str, Any]:
    """Delete an event."""
    service = _get_calendar_service(request)
    event = await service.get_event(event_id)
    if event is None:
        raise HTTPException(status_code=404, detail="Event not found")
    if event.user_id != user["user_id"]:
        raise HTTPException(status_code=403, detail="Access denied")

    deleted = await service.delete_event(event_id)
    return {"success": True, "deleted": deleted}


# ---------------------------------------------------------------------------
# Invitation endpoints
# ---------------------------------------------------------------------------


@router.post("/events/{event_id}/invitations")
async def create_invitation(
    event_id: str,
    body: CreateInvitationRequest,
    request: Request,
    user: dict = Depends(require_authenticated),
) -> dict[str, Any]:
    """Invite a user to an event by email."""
    service = _get_calendar_service(request)
    event = await service.get_event(event_id)
    if event is None:
        raise HTTPException(status_code=404, detail="Event not found")
    if event.user_id != user["user_id"]:
        raise HTTPException(status_code=403, detail="Only the event owner can invite")

    invitation = await service.create_invitation(
        event_id=event_id,
        inviter_user_id=user["user_id"],
        invitee_email=body.invitee_email,
    )
    return {"success": True, "invitation": invitation.to_dict()}


@router.post("/invitations/{invitation_id}/respond")
async def respond_to_invitation(
    invitation_id: str,
    body: RespondInvitationRequest,
    request: Request,
    user: dict = Depends(require_authenticated),
) -> dict[str, Any]:
    """Respond to an invitation (accept/decline/tentative)."""
    if body.status not in _VALID_INVITATION_STATUSES:
        raise HTTPException(
            status_code=400,
            detail=f"Invalid status. Must be one of: {sorted(_VALID_INVITATION_STATUSES)}",
        )

    service = _get_calendar_service(request)
    invitation = await service.get_invitation(invitation_id)
    if invitation is None:
        raise HTTPException(status_code=404, detail="Invitation not found")
    email = await service.get_user_email(user["user_id"])
    if email is None or invitation.invitee_email != email:
        raise HTTPException(status_code=403, detail="Only the invitee can respond")
    updated = await service.respond_to_invitation(invitation_id, body.status)
    if not updated:
        raise HTTPException(status_code=404, detail="Invitation not found")
    return {"success": True, "status": body.status}


@router.get("/invitations")
async def list_invitations(
    request: Request,
    user: dict = Depends(require_authenticated),
) -> dict[str, Any]:
    """List invitations for the current user (by email)."""
    service = _get_calendar_service(request)
    email = await service.get_user_email(user["user_id"])
    if email is None:
        return {"success": True, "invitations": [], "count": 0}
    invitations = await service.list_invitations(email)
    return {
        "success": True,
        "invitations": [inv.to_dict() for inv in invitations],
        "count": len(invitations),
    }


# ---------------------------------------------------------------------------
# User search endpoint (G5/A3)
# ---------------------------------------------------------------------------


@router.get("/users/search")
async def search_users(
    request: Request,
    q: str = Query(..., min_length=1),
    user: dict = Depends(require_authenticated),
) -> dict[str, Any]:
    """Search users by username or email. Returns top 10 matches.

    Only username and email are returned — never user_id or password
    fields (least-privilege, G5/A3).
    """
    service = _get_calendar_service(request)
    users = await service.search_users(q)
    return {"success": True, "users": users, "count": len(users)}


# ---------------------------------------------------------------------------
# Event Type endpoints
# ---------------------------------------------------------------------------


@router.get("/event-types")
async def list_event_types(
    request: Request,
    user: dict = Depends(require_authenticated),
) -> dict[str, Any]:
    """List all event types for the current user."""
    service = _get_calendar_service(request)
    types = await service.list_event_types(user["user_id"])
    return {
        "success": True,
        "event_types": [t.to_dict() for t in types],
        "count": len(types),
    }


@router.post("/event-types")
async def create_event_type(
    body: CreateEventTypeRequest,
    request: Request,
    user: dict = Depends(require_authenticated),
) -> dict[str, Any]:
    """Create a new event type."""
    service = _get_calendar_service(request)
    et = await service.create_event_type(
        user_id=user["user_id"],
        name=body.name,
        color=body.color,
    )
    return {"success": True, "event_type": et.to_dict()}


@router.patch("/event-types/{type_id}")
async def update_event_type(
    type_id: str,
    body: UpdateEventTypeRequest,
    request: Request,
    user: dict = Depends(require_authenticated),
) -> dict[str, Any]:
    """Update specific fields of an event type."""
    service = _get_calendar_service(request)
    et = await service.get_event_type(type_id)
    if et is None:
        raise HTTPException(status_code=404, detail="Event type not found")
    if et.user_id != user["user_id"]:
        raise HTTPException(status_code=403, detail="Access denied")
    fields: dict[str, Any] = {}
    for name, value in body.model_dump(exclude_unset=True).items():
        if value is not None:
            fields[name] = value
    if not fields:
        return {"success": True, "updated": False}
    updated = await service.update_event_type(type_id, fields)
    return {"success": True, "updated": updated}


# ---------------------------------------------------------------------------
# Tag endpoints
# ---------------------------------------------------------------------------


@router.get("/tags")
async def list_tags(
    request: Request,
    user: dict = Depends(require_authenticated),
) -> dict[str, Any]:
    """List all tags for the current user."""
    service = _get_calendar_service(request)
    tags = await service.list_tags(user["user_id"])
    return {"success": True, "tags": [t.to_dict() for t in tags], "count": len(tags)}


@router.post("/tags")
async def create_tag(
    body: CreateTagRequest,
    request: Request,
    user: dict = Depends(require_authenticated),
) -> dict[str, Any]:
    """Create a new tag."""
    service = _get_calendar_service(request)
    tag = await service.create_tag(user_id=user["user_id"], name=body.name)
    return {"success": True, "tag": tag.to_dict()}


# ---------------------------------------------------------------------------
# ICS import/export (U8)
# ---------------------------------------------------------------------------


@router.post("/import-ics")
async def import_ics(
    request: Request,
    file: UploadFile = File(...),
    user: dict = Depends(require_authenticated),
) -> dict[str, Any]:
    """Import events from an uploaded .ics file."""
    service = _get_calendar_service(request)
    content = await file.read()
    from agentkit.calendar.sync.ics_provider import ICSProvider  # lazy: avoid circular import

    provider = ICSProvider(service)
    try:
        result = await provider.import_ics(content, user["user_id"])
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    return {"success": True, **result}


@router.get("/export-ics")
async def export_ics(
    request: Request,
    start: str | None = Query(None),
    end: str | None = Query(None),
    user: dict = Depends(require_authenticated),
) -> Response:
    """Export the current user's events to a downloadable .ics file."""
    service = _get_calendar_service(request)
    events = await service.list_events(user_id=user["user_id"], start=start, end=end)
    from agentkit.calendar.sync.ics_provider import ICSProvider  # lazy: avoid circular import

    provider = ICSProvider(service)
    ics_bytes = provider.export_ics(events)
    return Response(
        content=ics_bytes,
        media_type="text/calendar",
        headers={"Content-Disposition": 'attachment; filename="calendar.ics"'},
    )
