"""Models for search and filtering operations."""

from typing import Dict, List, Optional, Any
from enum import Enum
from pydantic import BaseModel, Field


class SearchOperator(str, Enum):
    """Search operators for filtering."""
    EQUALS = "eq"
    NOT_EQUALS = "ne"
    GREATER_THAN = "gt"
    GREATER_THAN_OR_EQUAL = "ge"
    LESS_THAN = "lt"
    LESS_THAN_OR_EQUAL = "le"
    CONTAINS = "contains"
    STARTS_WITH = "startswith"
    ENDS_WITH = "endswith"


class SearchFilter(BaseModel):
    """Represents a single search filter criterion."""
    
    field_name: str = Field(..., description="Name or ID of the field to filter")
    operator: SearchOperator = Field(..., description="Comparison operator")
    value: Any = Field(..., description="Value to compare against")
    
    def to_odata_string(self) -> str:
        """
        Convert filter to OData query string format.
        
        Returns:
            OData filter string
            
        Example:
            >>> filter = SearchFilter(field_name="Status", operator=SearchOperator.EQUALS, value="Active")
            >>> filter.to_odata_string()
            "Status eq 'Active'"
        """
        if isinstance(self.value, str):
            value_str = f"'{self.value}'"
        else:
            value_str = str(self.value)
        
        if self.operator in [SearchOperator.CONTAINS, SearchOperator.STARTS_WITH, SearchOperator.ENDS_WITH]:
            return f"{self.operator.value}({self.field_name}, {value_str})"
        else:
            return f"{self.field_name} {self.operator.value} {value_str}"
    
    class Config:
        json_schema_extra = {
            "example": {
                "field_name": "Status",
                "operator": "eq",
                "value": "Active"
            }
        }


class SearchCriteria(BaseModel):
    """Complete search criteria for querying records."""
    
    filters: List[SearchFilter] = Field(default_factory=list)
    select_fields: Optional[List[str]] = Field(None, description="Fields to return")
    order_by: Optional[str] = Field(None, description="Field to order by")
    top: Optional[int] = Field(None, ge=1, description="Number of records to return")
    skip: Optional[int] = Field(None, ge=0, description="Number of records to skip")
    
    def build_query_string(self) -> str:
        """
        Build OData query string from criteria.
        
        Returns:
            Query string parameters
            
        Example:
            >>> criteria = SearchCriteria(
            ...     filters=[SearchFilter(field_name="Status", operator=SearchOperator.EQUALS, value="Active")],
            ...     top=10
            ... )
            >>> criteria.build_query_string()
            "$filter=Status eq 'Active'&$top=10"
        """
        parts = []
        
        if self.filters:
            filter_str = " and ".join([f.to_odata_string() for f in self.filters])
            parts.append(f"$filter={filter_str}")
        
        if self.select_fields:
            parts.append(f"$select={','.join(self.select_fields)}")
        
        if self.order_by:
            parts.append(f"$orderby={self.order_by}")
        
        if self.top is not None:
            parts.append(f"$top={self.top}")
        
        if self.skip is not None:
            parts.append(f"$skip={self.skip}")
        
        return "&".join(parts)


class SearchResponse(BaseModel):
    """Response from search operations."""
    
    records: List[Dict[str, Any]] = Field(default_factory=list)
    total_count: Optional[int] = None
    
    @property
    def count(self) -> int:
        """Get the number of records returned."""
        return len(self.records)