# services/external/app/api/calendar_operations.py
"""
Calendar Operations API - School calendars and tenant location context endpoints
"""

from fastapi import APIRouter, Depends, HTTPException, Query, Path, Body
from typing import List, Optional
from uuid import UUID
import structlog

from app.schemas.calendar import (
    SchoolCalendarResponse,
    SchoolCalendarListResponse,
    TenantLocationContextResponse,
    TenantLocationContextCreateRequest,
    CalendarCheckResponse
)
from app.registry.calendar_registry import CalendarRegistry, SchoolType
from app.repositories.calendar_repository import CalendarRepository
from app.cache.redis_wrapper import ExternalDataCache
from shared.routing.route_builder import RouteBuilder
from shared.auth.decorators import get_current_user_dep
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.database import get_db
from datetime import datetime, date

route_builder = RouteBuilder('external')
router = APIRouter(tags=["calendar-operations"])
logger = structlog.get_logger()

# Initialize cache
cache = ExternalDataCache()


# ===== School Calendar Endpoints =====

@router.get(
    route_builder.build_operations_route("cities/{city_id}/school-calendars"),
    response_model=SchoolCalendarListResponse
)
async def list_school_calendars_for_city(
    city_id: str = Path(..., description="City ID (e.g., 'madrid')"),
    school_type: Optional[str] = Query(None, description="Filter by school type"),
    academic_year: Optional[str] = Query(None, description="Filter by academic year"),
    db: AsyncSession = Depends(get_db)
):
    """List all available school calendars for a city"""
    try:
        repo = CalendarRepository(db)
        calendars = await repo.get_calendars_by_city(city_id, enabled_only=True)

        # Apply filters if provided
        if school_type:
            calendars = [c for c in calendars if c.school_type == school_type]
        if academic_year:
            calendars = [c for c in calendars if c.academic_year == academic_year]

        calendar_responses = [
            SchoolCalendarResponse(
                calendar_id=str(c.id),
                calendar_name=c.calendar_name,
                city_id=c.city_id,
                school_type=c.school_type,
                academic_year=c.academic_year,
                holiday_periods=c.holiday_periods,
                school_hours=c.school_hours,
                source=c.source,
                enabled=c.enabled
            )
            for c in calendars
        ]

        return SchoolCalendarListResponse(
            city_id=city_id,
            calendars=calendar_responses,
            total=len(calendar_responses)
        )

    except Exception as e:
        logger.error(
            "Error listing school calendars",
            city_id=city_id,
            error=str(e)
        )
        raise HTTPException(
            status_code=500,
            detail=f"Error retrieving school calendars: {str(e)}"
        )


@router.get(
    route_builder.build_operations_route("school-calendars/{calendar_id}"),
    response_model=SchoolCalendarResponse
)
async def get_school_calendar(
    calendar_id: UUID = Path(..., description="School calendar ID"),
    db: AsyncSession = Depends(get_db)
):
    """Get detailed information about a specific school calendar (cached)"""
    try:
        calendar_id_str = str(calendar_id)

        # Check cache first
        cached = await cache.get_cached_calendar(calendar_id_str)
        if cached:
            logger.debug("Returning cached calendar", calendar_id=calendar_id_str)
            return SchoolCalendarResponse(**cached)

        # Cache miss - fetch from database
        repo = CalendarRepository(db)
        calendar = await repo.get_calendar_by_id(calendar_id)

        if not calendar:
            raise HTTPException(status_code=404, detail="School calendar not found")

        response_data = {
            "calendar_id": str(calendar.id),
            "calendar_name": calendar.calendar_name,
            "city_id": calendar.city_id,
            "school_type": calendar.school_type,
            "academic_year": calendar.academic_year,
            "holiday_periods": calendar.holiday_periods,
            "school_hours": calendar.school_hours,
            "source": calendar.source,
            "enabled": calendar.enabled
        }

        # Cache the result
        await cache.set_cached_calendar(calendar_id_str, response_data)

        return SchoolCalendarResponse(**response_data)

    except HTTPException:
        raise
    except Exception as e:
        logger.error(
            "Error retrieving school calendar",
            calendar_id=str(calendar_id),
            error=str(e)
        )
        raise HTTPException(
            status_code=500,
            detail=f"Error retrieving school calendar: {str(e)}"
        )


@router.get(
    route_builder.build_operations_route("school-calendars/{calendar_id}/is-holiday"),
    response_model=CalendarCheckResponse
)
async def check_is_school_holiday(
    calendar_id: UUID = Path(..., description="School calendar ID"),
    check_date: str = Query(..., description="Date to check (ISO format: YYYY-MM-DD)"),
    db: AsyncSession = Depends(get_db)
):
    """Check if a specific date is a school holiday"""
    try:
        repo = CalendarRepository(db)
        calendar = await repo.get_calendar_by_id(calendar_id)

        if not calendar:
            raise HTTPException(status_code=404, detail="School calendar not found")

        # Parse the date
        try:
            date_obj = datetime.strptime(check_date, "%Y-%m-%d").date()
        except ValueError:
            raise HTTPException(
                status_code=400,
                detail="Invalid date format. Use YYYY-MM-DD"
            )

        # Check if date falls within any holiday period
        is_holiday = False
        holiday_name = None

        for period in calendar.holiday_periods:
            start = datetime.strptime(period["start_date"], "%Y-%m-%d").date()
            end = datetime.strptime(period["end_date"], "%Y-%m-%d").date()

            if start <= date_obj <= end:
                is_holiday = True
                holiday_name = period["name"]
                break

        return CalendarCheckResponse(
            date=check_date,
            is_holiday=is_holiday,
            holiday_name=holiday_name,
            calendar_id=str(calendar_id),
            calendar_name=calendar.calendar_name
        )

    except HTTPException:
        raise
    except Exception as e:
        logger.error(
            "Error checking holiday status",
            calendar_id=str(calendar_id),
            date=check_date,
            error=str(e)
        )
        raise HTTPException(
            status_code=500,
            detail=f"Error checking holiday status: {str(e)}"
        )


# ===== Tenant Location Context Endpoints =====

@router.get(
    route_builder.build_base_route("location-context"),
    response_model=TenantLocationContextResponse
)
async def get_tenant_location_context(
    tenant_id: str = Path(..., description="Tenant ID"),
    current_user: dict = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """Get location context for a tenant including school calendar assignment (cached)"""
    try:

        # Check cache first
        cached = await cache.get_cached_tenant_context(tenant_id)
        if cached:
            logger.debug("Returning cached tenant context", tenant_id=tenant_id)
            return TenantLocationContextResponse(**cached)

        # Cache miss - fetch from database
        repo = CalendarRepository(db)
        context = await repo.get_tenant_with_calendar(tenant_id)

        if not context:
            raise HTTPException(
                status_code=404,
                detail="Location context not found for this tenant"
            )

        # Cache the result
        await cache.set_cached_tenant_context(tenant_id_str, context)

        return TenantLocationContextResponse(**context)

    except HTTPException:
        raise
    except Exception as e:
        logger.error(
            "Error retrieving tenant location context",
            tenant_id=str(tenant_id),
            error=str(e)
        )
        raise HTTPException(
            status_code=500,
            detail=f"Error retrieving location context: {str(e)}"
        )


@router.post(
    route_builder.build_base_route("location-context"),
    response_model=TenantLocationContextResponse
)
async def create_or_update_tenant_location_context(
    request: TenantLocationContextCreateRequest,
    tenant_id: str = Path(..., description="Tenant ID"),
    current_user: dict = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """Create or update tenant location context"""
    try:

        # Convert to UUID for use with repository
        tenant_uuid = UUID(tenant_id)

        repo = CalendarRepository(db)

        # Validate calendar_id if provided
        if request.school_calendar_id:
            calendar = await repo.get_calendar_by_id(request.school_calendar_id)
            if not calendar:
                raise HTTPException(
                    status_code=400,
                    detail="Invalid school_calendar_id"
                )

        # Create or update context
        context_obj = await repo.create_or_update_tenant_location_context(
            tenant_id=tenant_uuid,
            city_id=request.city_id,
            school_calendar_id=request.school_calendar_id,
            neighborhood=request.neighborhood,
            local_events=request.local_events,
            notes=request.notes
        )

        # Invalidate cache since context was updated
        await cache.invalidate_tenant_context(tenant_id)

        # Get full context with calendar details
        context = await repo.get_tenant_with_calendar(tenant_uuid)

        # Cache the new context
        await cache.set_cached_tenant_context(tenant_id, context)

        return TenantLocationContextResponse(**context)

    except HTTPException:
        raise
    except Exception as e:
        logger.error(
            "Error creating/updating tenant location context",
            tenant_id=str(tenant_id),
            error=str(e)
        )
        raise HTTPException(
            status_code=500,
            detail=f"Error creating/updating location context: {str(e)}"
        )


@router.delete(
    route_builder.build_base_route("location-context"),
    status_code=204
)
async def delete_tenant_location_context(
    tenant_id: str = Path(..., description="Tenant ID"),
    current_user: dict = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """Delete tenant location context"""
    try:

        # Convert to UUID for use with repository
        tenant_uuid = UUID(tenant_id)

        repo = CalendarRepository(db)
        deleted = await repo.delete_tenant_location_context(tenant_uuid)

        if not deleted:
            raise HTTPException(
                status_code=404,
                detail="Location context not found"
            )

        return None

    except HTTPException:
        raise
    except Exception as e:
        logger.error(
            "Error deleting tenant location context",
            tenant_id=str(tenant_id),
            error=str(e)
        )
        raise HTTPException(
            status_code=500,
            detail=f"Error deleting location context: {str(e)}"
        )


# ===== Calendar Suggestion Endpoint =====

@router.post(
    route_builder.build_base_route("location-context/suggest-calendar")
)
async def suggest_calendar_for_tenant(
    tenant_id: str = Path(..., description="Tenant ID"),
    current_user: dict = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """
    Suggest an appropriate school calendar for a tenant based on location and POI data.

    This endpoint analyzes:
    - Tenant's city location
    - Detected schools nearby (from POI detection)
    - Available calendars for the city
    - Bakery-specific heuristics (primary schools = stronger morning rush)

    Returns a suggestion with confidence score and reasoning.
    Does NOT automatically assign - requires admin approval.
    """
    try:
        from app.utils.calendar_suggester import CalendarSuggester
        from app.repositories.poi_context_repository import POIContextRepository

        tenant_uuid = UUID(tenant_id)

        # Get tenant's location context
        calendar_repo = CalendarRepository(db)
        location_context = await calendar_repo.get_tenant_location_context(tenant_uuid)

        if not location_context:
            raise HTTPException(
                status_code=404,
                detail="Location context not found. Create location context first."
            )

        city_id = location_context.city_id

        # Get available calendars for city
        calendars_result = await calendar_repo.get_calendars_by_city(city_id, enabled_only=True)
        calendars = calendars_result.get("calendars", []) if calendars_result else []

        # Get POI context if available
        poi_repo = POIContextRepository(db)
        poi_context = await poi_repo.get_by_tenant_id(tenant_uuid)
        poi_data = poi_context.to_dict() if poi_context else None

        # Generate suggestion
        suggester = CalendarSuggester()
        suggestion = suggester.suggest_calendar_for_tenant(
            city_id=city_id,
            available_calendars=calendars,
            poi_context=poi_data,
            tenant_data=None  # Could include tenant info if needed
        )

        # Format for admin display
        admin_message = suggester.format_suggestion_for_admin(suggestion)

        logger.info(
            "Calendar suggestion generated",
            tenant_id=tenant_id,
            city_id=city_id,
            suggested_calendar=suggestion.get("suggested_calendar_id"),
            confidence=suggestion.get("confidence")
        )

        return {
            **suggestion,
            "admin_message": admin_message,
            "tenant_id": tenant_id,
            "current_calendar_id": str(location_context.school_calendar_id) if location_context.school_calendar_id else None
        }

    except HTTPException:
        raise
    except Exception as e:
        logger.error(
            "Error generating calendar suggestion",
            tenant_id=tenant_id,
            error=str(e),
            exc_info=True
        )
        raise HTTPException(
            status_code=500,
            detail=f"Error generating calendar suggestion: {str(e)}"
        )


# ===== Helper Endpoints =====

@router.get(
    route_builder.build_operations_route("calendars/registry"),
    response_model=List[SchoolCalendarResponse]
)
async def list_registry_calendars():
    """List all calendars from the CalendarRegistry (static configuration)"""
    calendars = CalendarRegistry.get_enabled_calendars()

    return [
        SchoolCalendarResponse(
            calendar_id=cal.calendar_id,
            calendar_name=cal.calendar_name,
            city_id=cal.city_id,
            school_type=cal.school_type.value,
            academic_year=cal.academic_year,
            holiday_periods=[
                {
                    "name": hp.name,
                    "start_date": hp.start_date,
                    "end_date": hp.end_date,
                    "description": hp.description
                }
                for hp in cal.holiday_periods
            ],
            school_hours={
                "morning_start": cal.school_hours.morning_start,
                "morning_end": cal.school_hours.morning_end,
                "has_afternoon_session": cal.school_hours.has_afternoon_session,
                "afternoon_start": cal.school_hours.afternoon_start,
                "afternoon_end": cal.school_hours.afternoon_end
            },
            source=cal.source,
            enabled=cal.enabled
        )
        for cal in calendars
    ]
