# services/external/app/repositories/calendar_repository.py
"""
Calendar Repository - Manages school calendars and tenant location contexts
"""

from typing import List, Dict, Any, Optional
from datetime import datetime
from sqlalchemy import select, and_, or_
from sqlalchemy.ext.asyncio import AsyncSession
import structlog
import uuid

from app.models.calendar import SchoolCalendar, TenantLocationContext

logger = structlog.get_logger()


class CalendarRepository:
    """Repository for school calendar and tenant location data"""

    def __init__(self, session: AsyncSession):
        self.session = session

    # ===== School Calendar Operations =====

    async def create_school_calendar(
        self,
        city_id: str,
        calendar_name: str,
        school_type: str,
        academic_year: str,
        holiday_periods: List[Dict[str, Any]],
        school_hours: Dict[str, Any],
        source: Optional[str] = None,
        enabled: bool = True
    ) -> SchoolCalendar:
        """Create a new school calendar"""
        try:
            calendar = SchoolCalendar(
                id=uuid.uuid4(),
                city_id=city_id,
                calendar_name=calendar_name,
                school_type=school_type,
                academic_year=academic_year,
                holiday_periods=holiday_periods,
                school_hours=school_hours,
                source=source,
                enabled=enabled
            )

            self.session.add(calendar)
            await self.session.commit()
            await self.session.refresh(calendar)

            logger.info(
                "School calendar created",
                calendar_id=str(calendar.id),
                city_id=city_id,
                school_type=school_type
            )

            return calendar

        except Exception as e:
            await self.session.rollback()
            logger.error(
                "Error creating school calendar",
                city_id=city_id,
                error=str(e)
            )
            raise

    async def get_calendar_by_id(
        self,
        calendar_id: uuid.UUID
    ) -> Optional[SchoolCalendar]:
        """Get school calendar by ID"""
        stmt = select(SchoolCalendar).where(SchoolCalendar.id == calendar_id)
        result = await self.session.execute(stmt)
        return result.scalar_one_or_none()

    async def get_calendars_by_city(
        self,
        city_id: str,
        enabled_only: bool = True
    ) -> List[SchoolCalendar]:
        """Get all school calendars for a city"""
        stmt = select(SchoolCalendar).where(SchoolCalendar.city_id == city_id)

        if enabled_only:
            stmt = stmt.where(SchoolCalendar.enabled == True)

        stmt = stmt.order_by(SchoolCalendar.academic_year.desc(), SchoolCalendar.school_type)

        result = await self.session.execute(stmt)
        return list(result.scalars().all())

    async def get_calendar_by_city_type_year(
        self,
        city_id: str,
        school_type: str,
        academic_year: str
    ) -> Optional[SchoolCalendar]:
        """Get specific calendar by city, type, and year"""
        stmt = select(SchoolCalendar).where(
            and_(
                SchoolCalendar.city_id == city_id,
                SchoolCalendar.school_type == school_type,
                SchoolCalendar.academic_year == academic_year,
                SchoolCalendar.enabled == True
            )
        )

        result = await self.session.execute(stmt)
        return result.scalar_one_or_none()

    async def update_calendar(
        self,
        calendar_id: uuid.UUID,
        **kwargs
    ) -> Optional[SchoolCalendar]:
        """Update school calendar"""
        try:
            calendar = await self.get_calendar_by_id(calendar_id)
            if not calendar:
                return None

            for key, value in kwargs.items():
                if hasattr(calendar, key):
                    setattr(calendar, key, value)

            calendar.updated_at = datetime.utcnow()

            await self.session.commit()
            await self.session.refresh(calendar)

            logger.info(
                "School calendar updated",
                calendar_id=str(calendar_id),
                fields=list(kwargs.keys())
            )

            return calendar

        except Exception as e:
            await self.session.rollback()
            logger.error(
                "Error updating school calendar",
                calendar_id=str(calendar_id),
                error=str(e)
            )
            raise

    async def delete_calendar(self, calendar_id: uuid.UUID) -> bool:
        """Delete school calendar"""
        try:
            calendar = await self.get_calendar_by_id(calendar_id)
            if not calendar:
                return False

            await self.session.delete(calendar)
            await self.session.commit()

            logger.info("School calendar deleted", calendar_id=str(calendar_id))
            return True

        except Exception as e:
            await self.session.rollback()
            logger.error(
                "Error deleting school calendar",
                calendar_id=str(calendar_id),
                error=str(e)
            )
            raise

    # ===== Tenant Location Context Operations =====

    async def create_or_update_tenant_location_context(
        self,
        tenant_id: uuid.UUID,
        city_id: str,
        school_calendar_id: Optional[uuid.UUID] = None,
        neighborhood: Optional[str] = None,
        local_events: Optional[List[Dict[str, Any]]] = None,
        notes: Optional[str] = None
    ) -> TenantLocationContext:
        """Create or update tenant location context"""
        try:
            # Check if context exists
            existing = await self.get_tenant_location_context(tenant_id)

            if existing:
                # Update existing
                existing.city_id = city_id
                if school_calendar_id is not None:
                    existing.school_calendar_id = school_calendar_id
                if neighborhood is not None:
                    existing.neighborhood = neighborhood
                if local_events is not None:
                    existing.local_events = local_events
                if notes is not None:
                    existing.notes = notes
                existing.updated_at = datetime.utcnow()

                await self.session.commit()
                await self.session.refresh(existing)

                logger.info(
                    "Tenant location context updated",
                    tenant_id=str(tenant_id)
                )

                return existing
            else:
                # Create new
                context = TenantLocationContext(
                    tenant_id=tenant_id,
                    city_id=city_id,
                    school_calendar_id=school_calendar_id,
                    neighborhood=neighborhood,
                    local_events=local_events or [],
                    notes=notes
                )

                self.session.add(context)
                await self.session.commit()
                await self.session.refresh(context)

                logger.info(
                    "Tenant location context created",
                    tenant_id=str(tenant_id),
                    city_id=city_id
                )

                return context

        except Exception as e:
            await self.session.rollback()
            logger.error(
                "Error creating/updating tenant location context",
                tenant_id=str(tenant_id),
                error=str(e)
            )
            raise

    async def get_tenant_location_context(
        self,
        tenant_id: uuid.UUID
    ) -> Optional[TenantLocationContext]:
        """Get tenant location context"""
        stmt = select(TenantLocationContext).where(
            TenantLocationContext.tenant_id == tenant_id
        )
        result = await self.session.execute(stmt)
        return result.scalar_one_or_none()

    async def get_tenant_with_calendar(
        self,
        tenant_id: uuid.UUID
    ) -> Optional[Dict[str, Any]]:
        """Get tenant location context with full calendar details"""
        context = await self.get_tenant_location_context(tenant_id)
        if not context:
            return None

        result = {
            "tenant_id": str(context.tenant_id),
            "city_id": context.city_id,
            "neighborhood": context.neighborhood,
            "local_events": context.local_events,
            "notes": context.notes,
            "calendar": None
        }

        if context.school_calendar_id:
            calendar = await self.get_calendar_by_id(context.school_calendar_id)
            if calendar:
                result["calendar"] = {
                    "calendar_id": str(calendar.id),
                    "calendar_name": calendar.calendar_name,
                    "school_type": calendar.school_type,
                    "academic_year": calendar.academic_year,
                    "holiday_periods": calendar.holiday_periods,
                    "school_hours": calendar.school_hours,
                    "source": calendar.source
                }

        return result

    async def delete_tenant_location_context(
        self,
        tenant_id: uuid.UUID
    ) -> bool:
        """Delete tenant location context"""
        try:
            context = await self.get_tenant_location_context(tenant_id)
            if not context:
                return False

            await self.session.delete(context)
            await self.session.commit()

            logger.info(
                "Tenant location context deleted",
                tenant_id=str(tenant_id)
            )
            return True

        except Exception as e:
            await self.session.rollback()
            logger.error(
                "Error deleting tenant location context",
                tenant_id=str(tenant_id),
                error=str(e)
            )
            raise

    # ===== Helper Methods =====

    async def get_all_tenants_for_calendar(
        self,
        calendar_id: uuid.UUID
    ) -> List[TenantLocationContext]:
        """Get all tenants using a specific calendar"""
        stmt = select(TenantLocationContext).where(
            TenantLocationContext.school_calendar_id == calendar_id
        )
        result = await self.session.execute(stmt)
        return list(result.scalars().all())
