"""
Common schema definitions and utilities.
"""

from typing import Any, Dict, Optional
from pydantic import BaseModel, field_validator, ConfigDict, Field
from sqlalchemy.orm import Query
from src.core.utils.enums import AddressTypes
from pydantic.types import Json
from datetime import datetime


class BaseSchema(BaseModel):
    model_config = ConfigDict(from_attributes=True)
    
    @field_validator("*", mode="before")
    @classmethod
    def evaluate_lazy_columns(cls, v):
        if isinstance(v, Query):
            return v.all()
        return v


class CommonResponse(BaseModel):
    """Common response structure for API endpoints."""
    
    success: bool = Field(True, description="Indicates if the request was successful")
    message: Optional[str] = Field(None, description="Optional message")
    data: Optional[Any] = Field(None, description="Response data")


class ErrorResponse(BaseModel):
    """Error response structure."""
    
    success: bool = Field(False, description="Always false for error responses")
    error: str = Field(..., description="Error message")
    details: Optional[Dict[str, Any]] = Field(None, description="Additional error details")


class PaginationMeta(BaseModel):
    """Pagination metadata."""
    
    total: int = Field(..., description="Total number of items")
    page: int = Field(..., description="Current page number")
    per_page: int = Field(..., description="Items per page")
    total_pages: int = Field(..., description="Total number of pages")
    has_next: bool = Field(..., description="Whether there is a next page")
    has_previous: bool = Field(..., description="Whether there is a previous page")


class PaginatedResponse(BaseModel):
    """Paginated response structure."""
    
    data: list[Any] = Field(..., description="List of items")
    meta: PaginationMeta = Field(..., description="Pagination metadata")
    links: Dict[str, Optional[str]] = Field(..., description="Navigation links")


class AddressBase(BaseSchema):
    address_line_1: str = Field(description="address line 1")
    address_line_2: Optional[str] = Field(default=None, description="address line 2")
    zipcode: str = Field(description="ZIP Code of address")
    city: str = Field(description="ISO code of residing city")
    state: str = Field(description="ISO code of residing state")
    country: Optional[str] = Field(default="US", description="ISO code of residing country")
    address_type: Optional[AddressTypes] = Field(
        description="Type of address being used", default=AddressTypes.BILLING
    )
    attention: Optional[str] = Field(None, description="Attention of the address")
    location_data: Optional[Json] = Field(None, description="location json data")
    lat: Optional[float] = Field(None, description="location latitude")
    lng: Optional[float] = Field(None, description="location longitude")
    use_as_default: Optional[bool] = Field(
        default=False,
        description="Whether use this address as default address or not. Applicable when address_type is `shipping`"
    )
    is_active: Optional[bool] = Field(
        default=True,
        description="Whether this address is being used by the customer or not?"
    )


class AddressCreateRequestSchema(AddressBase):
    """Schema for creating new addresses."""
    model_config = ConfigDict(
        from_attributes=True,
        json_schema_extra={
            "example": {
                "address_line_1": "123 Main Street",
                "address_line_2": "Apartment 4B",
                "zipcode": "10001",
                "city": "New York",
                "state": "NY",
                "country": "US",
                "address_type": "billing",
                "attention": "John Doe",
                "use_as_default": True,
                "is_active": True,
                "lat": 40.7589,
                "lng": -73.9851
            }
        }
    )


class AddressSchema(AddressBase):
    id: int = Field(description="Id of Address resource")
    created_at: Optional[datetime] = Field(
        default=None,
        description="Created date and time as unix timestamp"
    )
    updated_at: Optional[datetime] = Field(
        default=None,
        description="Updated date and time as unix timestamp"
    )
    deleted_at: Optional[datetime] = Field(
        default=None,
        description="Deleted date and time as unix timestamp"
    )