"""
Availability Service - Resource capacity checking with concurrency management and time blocks
"""
from django.contrib.contenttypes.models import ContentType
from django.db.models import Q
from .models import Event, Participant, Resource, TimeBlock


class AvailabilityService:
    """
    Service for checking resource availability with concurrency limits and time blocks.

    CRITICAL Features:
    - Handles max_concurrent_events==0 as unlimited capacity
    - Filters out CANCELED events to prevent ghost bookings
    - Uses correct overlap logic: start < query_end AND end > query_start
    - Checks business-level and resource-level time blocks
    - Returns soft block warnings separately from hard blocks
    """

    @staticmethod
    def check_availability(resource, start_time, end_time, exclude_event_id=None):
        """
        Check if resource has capacity for a new/updated event.

        Args:
            resource (Resource): The resource to check
            start_time (datetime): Proposed event start
            end_time (datetime): Proposed event end
            exclude_event_id (int, optional): Event ID to exclude (when updating)

        Returns:
            tuple: (is_available: bool, reason: str, soft_block_warnings: list)
            - is_available: False if hard-blocked or capacity exceeded
            - reason: Human-readable explanation
            - soft_block_warnings: List of soft block warnings (can be overridden)
        """
        soft_block_warnings = []

        # Step 1: Check time blocks (business-level first, then resource-level)
        block_result = AvailabilityService._check_time_blocks(
            resource, start_time, end_time
        )

        if block_result['hard_blocked']:
            return False, block_result['reason'], []

        soft_block_warnings.extend(block_result['soft_warnings'])

        # Step 2: Calculate search window with buffer
        query_start = start_time - resource.buffer_duration
        query_end = end_time + resource.buffer_duration

        # Step 3: Find all events for this resource
        resource_content_type = ContentType.objects.get_for_model(Resource)

        resource_participants = Participant.objects.filter(
            content_type=resource_content_type,
            object_id=resource.id,
            role=Participant.Role.RESOURCE
        ).select_related('event')

        # Step 4: Filter for overlapping events
        overlapping_events = []
        for participant in resource_participants:
            event = participant.event

            # Skip if this is the event being updated
            # CRITICAL: Convert exclude_event_id to int for comparison (frontend may send string)
            if exclude_event_id and event.id == int(exclude_event_id):
                continue

            # CRITICAL: Skip cancelled events (prevents ghost bookings)
            if event.status == Event.Status.CANCELED:
                continue

            # CRITICAL: Check overlap using correct logic
            # Overlap exists when: event.start < query_end AND event.end > query_start
            if event.start_time < query_end and event.end_time > query_start:
                overlapping_events.append(event)

        current_count = len(overlapping_events)

        # Step 5: Check capacity limit

        # CRITICAL: Handle infinite capacity (0 = unlimited)
        if resource.max_concurrent_events == 0:
            return True, "Unlimited capacity resource", soft_block_warnings

        # Check if we've hit the limit
        if current_count >= resource.max_concurrent_events:
            return False, (
                f"Resource capacity exceeded. "
                f"{current_count}/{resource.max_concurrent_events} slots occupied."
            ), []

        # Available!
        return True, f"Available ({current_count + 1}/{resource.max_concurrent_events} slots)", soft_block_warnings

    @staticmethod
    def _check_time_blocks(resource, start_time, end_time):
        """
        Check if a time period is blocked by any time blocks.

        Checks both business-level blocks (resource=null) and resource-level blocks.
        Business-level blocks are checked first as they apply to all resources.

        Args:
            resource (Resource): The resource to check
            start_time (datetime): Proposed event start
            end_time (datetime): Proposed event end

        Returns:
            dict: {
                'hard_blocked': bool,
                'reason': str,
                'soft_warnings': list of warning strings
            }
        """
        result = {
            'hard_blocked': False,
            'reason': '',
            'soft_warnings': []
        }

        # Get active time blocks (business-level + resource-level)
        blocks = TimeBlock.objects.filter(
            Q(resource__isnull=True) | Q(resource=resource),
            is_active=True
        ).order_by('resource')  # Business blocks first (null sorts first)

        for block in blocks:
            # Check if this block applies to the requested datetime range
            if block.blocks_datetime_range(start_time, end_time):
                if block.block_type == TimeBlock.BlockType.HARD:
                    # Hard block - immediately return unavailable
                    level = "Business closed" if block.resource is None else f"{resource.name} unavailable"
                    result['hard_blocked'] = True
                    result['reason'] = f"{level}: {block.title}"
                    return result
                else:
                    # Soft block - add warning but continue
                    level = "Business advisory" if block.resource is None else f"{resource.name} advisory"
                    result['soft_warnings'].append(f"{level}: {block.title}")

        return result

    @staticmethod
    def check_availability_simple(resource, start_time, end_time, exclude_event_id=None):
        """
        Simple availability check that returns just (bool, str) for backwards compatibility.

        Use check_availability() for full soft block warning support.
        """
        is_available, reason, _ = AvailabilityService.check_availability(
            resource, start_time, end_time, exclude_event_id
        )
        return is_available, reason
