from pydantic import BaseModel, Field, field_validator, ConfigDict
from typing import Optional, List
from datetime import datetime, date, time
from enum import Enum


class SeasonType(str, Enum):
    PEAK = "peak"
    OFF_PEAK = "off_peak"
    SHOULDER = "shoulder"


class AmenityType(str, Enum):
    ACCOMMODATION = "accommodation"
    TRANSPORT = "transport"
    TOUR = "tour"
    GENERAL = "general"


class DifficultyLevel(str, Enum):
    EASY = "easy"
    MODERATE = "moderate"
    CHALLENGING = "challenging"
    DIFFICULT = "difficult"


# Category Schemas
class CategoryBase(BaseModel):
    name: str = Field(..., min_length=1, max_length=255)
    slug: str = Field(..., min_length=1, max_length=255)
    description: Optional[str] = None
    icon_url: Optional[str] = Field(None, max_length=500)
    is_active: bool = True
    display_order: Optional[int] = None


class CategoryCreate(CategoryBase):
    @field_validator("slug")
    @classmethod
    def validate_slug(cls, v: str) -> str:
        if not v.replace("-", "").replace("_", "").isalnum():
            raise ValueError("Slug must contain only alphanumeric characters, hyphens, and underscores")
        return v.lower()


class CategoryUpdate(BaseModel):
    name: Optional[str] = Field(None, min_length=1, max_length=255)
    slug: Optional[str] = Field(None, min_length=1, max_length=255)
    description: Optional[str] = None
    icon_url: Optional[str] = Field(None, max_length=500)
    is_active: Optional[bool] = None
    display_order: Optional[int] = None

    @field_validator("slug")
    @classmethod
    def validate_slug(cls, v: Optional[str]) -> Optional[str]:
        if v is not None:
            if not v.replace("-", "").replace("_", "").isalnum():
                raise ValueError("Slug must contain only alphanumeric characters, hyphens, and underscores")
            return v.lower()
        return v


class CategoryResponse(CategoryBase):
    id: str
    created_at: datetime
    updated_at: datetime

    model_config = ConfigDict(from_attributes=True)


# Destination Schemas
class DestinationBase(BaseModel):
    name: str = Field(..., min_length=1, max_length=255)
    slug: str = Field(..., min_length=1, max_length=255)
    country: str = Field(..., min_length=1, max_length=100)
    state: Optional[str] = Field(None, max_length=100)
    city: Optional[str] = Field(None, max_length=100)
    description: Optional[str] = None
    attractions: Optional[str] = None
    best_time_to_visit: Optional[str] = Field(None, max_length=255)
    climate_info: Optional[str] = None
    latitude: Optional[float] = None
    longitude: Optional[float] = None
    image_url: Optional[str] = Field(None, max_length=500)
    is_active: bool = True


class DestinationCreate(DestinationBase):
    @field_validator("slug")
    @classmethod
    def validate_slug(cls, v: str) -> str:
        if not v.replace("-", "").replace("_", "").isalnum():
            raise ValueError("Slug must contain only alphanumeric characters, hyphens, and underscores")
        return v.lower()

    @field_validator("latitude")
    @classmethod
    def validate_latitude(cls, v: Optional[float]) -> Optional[float]:
        if v is not None and (v < -90 or v > 90):
            raise ValueError("Latitude must be between -90 and 90")
        return v

    @field_validator("longitude")
    @classmethod
    def validate_longitude(cls, v: Optional[float]) -> Optional[float]:
        if v is not None and (v < -180 or v > 180):
            raise ValueError("Longitude must be between -180 and 180")
        return v


class DestinationUpdate(BaseModel):
    name: Optional[str] = Field(None, min_length=1, max_length=255)
    slug: Optional[str] = Field(None, min_length=1, max_length=255)
    country: Optional[str] = Field(None, min_length=1, max_length=100)
    state: Optional[str] = Field(None, max_length=100)
    city: Optional[str] = Field(None, max_length=100)
    description: Optional[str] = None
    attractions: Optional[str] = None
    best_time_to_visit: Optional[str] = Field(None, max_length=255)
    climate_info: Optional[str] = None
    latitude: Optional[float] = None
    longitude: Optional[float] = None
    image_url: Optional[str] = Field(None, max_length=500)
    is_active: Optional[bool] = None

    @field_validator("slug")
    @classmethod
    def validate_slug(cls, v: Optional[str]) -> Optional[str]:
        if v is not None:
            if not v.replace("-", "").replace("_", "").isalnum():
                raise ValueError("Slug must contain only alphanumeric characters, hyphens, and underscores")
            return v.lower()
        return v

    @field_validator("latitude")
    @classmethod
    def validate_latitude(cls, v: Optional[float]) -> Optional[float]:
        if v is not None and (v < -90 or v > 90):
            raise ValueError("Latitude must be between -90 and 90")
        return v

    @field_validator("longitude")
    @classmethod
    def validate_longitude(cls, v: Optional[float]) -> Optional[float]:
        if v is not None and (v < -180 or v > 180):
            raise ValueError("Longitude must be between -180 and 180")
        return v


class DestinationResponse(DestinationBase):
    id: str
    created_at: datetime
    updated_at: datetime

    model_config = ConfigDict(from_attributes=True)


# Season Schemas
class SeasonBase(BaseModel):
    name: str = Field(..., min_length=1, max_length=255)
    season_type: SeasonType
    start_date: date
    end_date: date
    price_multiplier: float = Field(..., ge=0)
    description: Optional[str] = None


class SeasonCreate(SeasonBase):
    @field_validator("end_date")
    @classmethod
    def validate_end_date(cls, v: date, info) -> date:
        if "start_date" in info.data and v <= info.data["start_date"]:
            raise ValueError("End date must be after start date")
        return v


class SeasonUpdate(BaseModel):
    name: Optional[str] = Field(None, min_length=1, max_length=255)
    season_type: Optional[SeasonType] = None
    start_date: Optional[date] = None
    end_date: Optional[date] = None
    price_multiplier: Optional[float] = Field(None, ge=0)
    description: Optional[str] = None


class SeasonResponse(SeasonBase):
    id: str
    created_at: datetime
    updated_at: datetime

    model_config = ConfigDict(from_attributes=True)


# Amenity Schemas
class AmenityBase(BaseModel):
    name: str = Field(..., min_length=1, max_length=255)
    description: Optional[str] = None
    icon_url: Optional[str] = Field(None, max_length=500)
    amenity_type: AmenityType


class AmenityCreate(AmenityBase):
    pass


class AmenityUpdate(BaseModel):
    name: Optional[str] = Field(None, min_length=1, max_length=255)
    description: Optional[str] = None
    icon_url: Optional[str] = Field(None, max_length=500)
    amenity_type: Optional[AmenityType] = None


class AmenityResponse(AmenityBase):
    id: str
    created_at: datetime
    updated_at: datetime

    model_config = ConfigDict(from_attributes=True)


# Tourpackage Schemas
class TourpackageBase(BaseModel):
    category_id: str
    name: str = Field(..., min_length=1, max_length=255)
    slug: str = Field(..., min_length=1, max_length=255)
    description: Optional[str] = None
    duration_days: int = Field(..., ge=0)
    duration_nights: int = Field(..., ge=0)
    difficulty_level: Optional[DifficultyLevel] = None
    min_age: Optional[int] = Field(None, ge=0)
    max_age: Optional[int] = Field(None, ge=0)
    min_group_size: int = Field(..., ge=0)
    max_group_size: int = Field(..., ge=0)
    base_price: float = Field(..., ge=0)
    child_price: Optional[float] = Field(None, ge=0)
    single_supplement: Optional[float] = Field(None, ge=0)
    inclusions: Optional[str] = None
    exclusions: Optional[str] = None
    terms_and_conditions: Optional[str] = None
    cancellation_policy: Optional[str] = None
    is_active: bool = True
    is_featured: bool = False
    image_url: Optional[str] = Field(None, max_length=500)


class TourpackageCreate(TourpackageBase):
    @field_validator("slug")
    @classmethod
    def validate_slug(cls, v: str) -> str:
        if not v.replace("-", "").replace("_", "").isalnum():
            raise ValueError("Slug must contain only alphanumeric characters, hyphens, and underscores")
        return v.lower()

    @field_validator("max_group_size")
    @classmethod
    def validate_max_group_size(cls, v: int, info) -> int:
        if "min_group_size" in info.data and v < info.data["min_group_size"]:
            raise ValueError("Maximum group size must be greater than or equal to minimum group size")
        return v

    @field_validator("max_age")
    @classmethod
    def validate_max_age(cls, v: Optional[int], info) -> Optional[int]:
        if v is not None and "min_age" in info.data and info.data["min_age"] is not None:
            if v < info.data["min_age"]:
                raise ValueError("Maximum age must be greater than or equal to minimum age")
        return v


class TourpackageUpdate(BaseModel):
    category_id: Optional[str] = None
    name: Optional[str] = Field(None, min_length=1, max_length=255)
    slug: Optional[str] = Field(None, min_length=1, max_length=255)
    description: Optional[str] = None
    duration_days: Optional[int] = Field(None, ge=0)
    duration_nights: Optional[int] = Field(None, ge=0)
    difficulty_level: Optional[DifficultyLevel] = None
    min_age: Optional[int] = Field(None, ge=0)
    max_age: Optional[int] = Field(None, ge=0)
    min_group_size: Optional[int] = Field(None, ge=0)
    max_group_size: Optional[int] = Field(None, ge=0)
    base_price: Optional[float] = Field(None, ge=0)
    child_price: Optional[float] = Field(None, ge=0)
    single_supplement: Optional[float] = Field(None, ge=0)
    inclusions: Optional[str] = None
    exclusions: Optional[str] = None
    terms_and_conditions: Optional[str] = None
    cancellation_policy: Optional[str] = None
    is_active: Optional[bool] = None
    is_featured: Optional[bool] = None
    image_url: Optional[str] = Field(None, max_length=500)

    @field_validator("slug")
    @classmethod
    def validate_slug(cls, v: Optional[str]) -> Optional[str]:
        if v is not None:
            if not v.replace("-", "").replace("_", "").isalnum():
                raise ValueError("Slug must contain only alphanumeric characters, hyphens, and underscores")
            return v.lower()
        return v


class TourpackageResponse(TourpackageBase):
    id: str
    rating: Optional[float] = None
    total_reviews: int
    created_at: datetime
    updated_at: datetime

    model_config = ConfigDict(from_attributes=True)


# Tourpackagedestination Schemas
class TourpackagedestinationBase(BaseModel):
    tour_package_id: str
    destination_id: str
    visit_order: int = Field(..., ge=0)
    duration_hours: Optional[int] = Field(None, ge=0)


class TourpackagedestinationCreate(TourpackagedestinationBase):
    pass


class TourpackagedestinationUpdate(BaseModel):
    tour_package_id: Optional[str] = None
    destination_id: Optional[str] = None
    visit_order: Optional[int] = Field(None, ge=0)
    duration_hours: Optional[int] = Field(None, ge=0)


class TourpackagedestinationResponse(TourpackagedestinationBase):
    id: str
    created_at: datetime
    updated_at: datetime

    model_config = ConfigDict(from_attributes=True)


# Tourpackageamenity Schemas
class TourpackageamenityBase(BaseModel):
    tour_package_id: str
    amenity_id: str


class TourpackageamenityCreate(TourpackageamenityBase):
    pass


class TourpackageamenityUpdate(BaseModel):
    tour_package_id: Optional[str] = None
    amenity_id: Optional[str] = None


class TourpackageamenityResponse(TourpackageamenityBase):
    id: str
    created_at: datetime
    updated_at: datetime

    model_config = ConfigDict(from_attributes=True)


# Itinerary Schemas
class ItineraryBase(BaseModel):
    tour_package_id: str
    day_number: int = Field(..., ge=1)
    title: str = Field(..., min_length=1, max_length=255)
    description: Optional[str] = None
    activities: Optional[str] = None
    meals_included: Optional[str] = Field(None, max_length=255)
    accommodation_type: Optional[str] = Field(None, max_length=100)
    start_time: Optional[time] = None
    end_time: Optional[time] = None


class ItineraryCreate(ItineraryBase):
    pass


class ItineraryUpdate(BaseModel):
    tour_package_id: Optional[str] = None
    day_number: Optional[int] = Field(None, ge=1)
    title: Optional[str] = Field(None, min_length=1, max_length=255)
    description: Optional[str] = None
    activities: Optional[str] = None
    meals_included: Optional[str] = Field(None, max_length=255)
    accommodation_type: Optional[str] = Field(None, max_length=100)
    start_time: Optional[time] = None
    end_time: Optional[time] = None


class ItineraryResponse(ItineraryBase):
    id: str
    created_at: datetime
    updated_at: datetime

    model_config = ConfigDict(from_attributes=True)


# Detail Response Schemas
class DestinationInPackageResponse(BaseModel):
    id: str
    name: str
    country: str
    state: Optional[str] = None
    city: Optional[str] = None
    visit_order: int
    duration_hours: Optional[int] = None

    model_config = ConfigDict(from_attributes=True)


class AmenityInPackageResponse(BaseModel):
    id: str
    name: str
    amenity_type: str

    model_config = ConfigDict(from_attributes=True)


class TourpackageDetailResponse(BaseModel):
    id: str
    category_id: str
    category_name: str
    name: str
    slug: str
    description: Optional[str] = None
    duration_days: int
    duration_nights: int
    difficulty_level: Optional[str] = None
    min_age: Optional[int] = None
    max_age: Optional[int] = None
    min_group_size: int
    max_group_size: int
    base_price: float
    child_price: Optional[float] = None
    single_supplement: Optional[float] = None
    inclusions: Optional[str] = None
    exclusions: Optional[str] = None
    terms_and_conditions: Optional[str] = None
    cancellation_policy: Optional[str] = None
    is_active: bool
    is_featured: bool
    rating: Optional[float] = None
    total_reviews: int
    image_url: Optional[str] = None
    created_at: datetime
    updated_at: datetime
    itineraries: List[ItineraryResponse]
    destinations: List[DestinationInPackageResponse]
    amenities: List[AmenityInPackageResponse]

    model_config = ConfigDict(from_attributes=True)