import json
from pydantic import BaseModel, model_validator, field_validator
from typing import Optional, List, Any


class CategoryCreate(BaseModel):
    name: str
    color: Optional[str] = None
    sort_order: int = 0
    parent_id: Optional[int] = None
    general_sort_order: int = 0
    auto_expanded: bool = False


class CategoryUpdate(BaseModel):
    name: Optional[str] = None
    color: Optional[str] = None
    sort_order: Optional[int] = None
    parent_id: Optional[int] = None
    general_sort_order: Optional[int] = None
    auto_expanded: Optional[bool] = None


class CategoryOut(BaseModel):
    id: int
    name: str
    color: Optional[str] = None
    sort_order: int = 0
    parent_id: Optional[int] = None
    general_sort_order: int = 0
    auto_expanded: bool = False

    model_config = {"from_attributes": True}


class CategoryReorderItem(BaseModel):
    id: int
    sort_order: int


class SubcategoryReorderItem(BaseModel):
    id: int
    sort_order: int  # position among subcategories within the parent


class ParentGeneralReorderItem(BaseModel):
    id: int  # parent category id
    general_sort_order: int


# ── Quick Options ─────────────────────────────────────────────────────────────

class ProductQuickOptionCreate(BaseModel):
    name: str
    price: float = 0.0
    allow_multiple: bool = False
    sort_order: int = 0
    is_favorite: bool = False
    favorite_sort_order: int = 0
    is_compact: bool = False


class ProductQuickOptionOut(BaseModel):
    id: int
    product_id: int
    name: str
    price: float = 0.0
    allow_multiple: bool = False
    sort_order: int = 0
    is_favorite: bool = False
    favorite_sort_order: int = 0
    is_compact: bool = False

    model_config = {"from_attributes": True}


# ── Options ──────────────────────────────────────────────────────────────────

class OptionSubChoice(BaseModel):
    name: str
    extra_cost: float = 0.0
    is_default: bool = False


class ProductOptionBase(BaseModel):
    name: str
    extra_cost: float = 0.0
    allow_multiple: bool = False
    is_favorite: bool = False
    favorite_sort_order: int = 0


class ProductOptionCreate(ProductOptionBase):
    sub_choices: List[OptionSubChoice] = []


class ProductOptionOut(ProductOptionBase):
    id: int
    product_id: int
    sub_choices: List[OptionSubChoice] = []

    model_config = {"from_attributes": True}

    @model_validator(mode='before')
    @classmethod
    def parse_option_sub_choices(cls, data: Any) -> Any:
        if hasattr(data, 'sub_choices'):
            raw = data.sub_choices
            parsed = json.loads(raw) if isinstance(raw, str) else []
            return {
                'id': data.id,
                'product_id': data.product_id,
                'name': data.name,
                'extra_cost': data.extra_cost,
                'allow_multiple': getattr(data, 'allow_multiple', False) or False,
                'sub_choices': parsed,
                'is_favorite': getattr(data, 'is_favorite', False) or False,
                'favorite_sort_order': getattr(data, 'favorite_sort_order', 0) or 0,
            }
        return data


# ── Ingredients ───────────────────────────────────────────────────────────────

class ProductIngredientBase(BaseModel):
    name: str
    extra_cost: float = 0.0
    is_favorite: bool = False
    favorite_sort_order: int = 0


class ProductIngredientCreate(ProductIngredientBase):
    pass


class ProductIngredientOut(ProductIngredientBase):
    id: int
    product_id: int

    model_config = {"from_attributes": True}


# ── Sub-choices (nested under a preference choice) ────────────────────────────

class SubChoice(BaseModel):
    name: str
    extra_cost: float = 0.0
    is_default: bool = False


# ── Shared subset (set-level, shown for all non-disabling choices) ─────────────

class SharedSubsetChoice(BaseModel):
    name: str
    extra_cost: float = 0.0
    is_default: bool = False


class SharedSubset(BaseModel):
    name: str
    choices: List[SharedSubsetChoice] = []


# ── Preferences ───────────────────────────────────────────────────────────────

class PreferenceChoiceCreate(BaseModel):
    name: str
    extra_cost: float = 0.0
    sub_choices: List[SubChoice] = []
    disables_subset: bool = False


class PreferenceChoiceOut(BaseModel):
    id: int
    set_id: int
    name: str
    extra_cost: float = 0.0
    sub_choices: List[SubChoice] = []
    disables_subset: bool = False

    model_config = {"from_attributes": True}

    @model_validator(mode='before')
    @classmethod
    def parse_sub_choices(cls, data: Any) -> Any:
        if hasattr(data, 'sub_choices'):
            raw = data.sub_choices
            if isinstance(raw, str):
                try:
                    parsed = json.loads(raw)
                except Exception:
                    parsed = []
            else:
                parsed = []
            return {
                'id': data.id,
                'set_id': data.set_id,
                'name': data.name,
                'extra_cost': data.extra_cost,
                'sub_choices': parsed,
                'disables_subset': data.disables_subset or False,
            }
        return data


class PreferenceSetCreate(BaseModel):
    name: str
    choices: List[PreferenceChoiceCreate] = []
    default_choice_index: Optional[int] = None  # index into choices (0-based)
    shared_subset: Optional[SharedSubset] = None
    is_favorite: bool = False
    favorite_sort_order: int = 0


class PreferenceSetOut(BaseModel):
    id: int
    product_id: int
    name: str
    choices: List[PreferenceChoiceOut] = []
    default_choice_id: Optional[int] = None
    shared_subset: Optional[SharedSubset] = None
    is_favorite: bool = False
    favorite_sort_order: int = 0

    model_config = {"from_attributes": True}

    @model_validator(mode='before')
    @classmethod
    def parse_shared_subset(cls, data: Any) -> Any:
        if hasattr(data, 'shared_subset'):
            raw = data.shared_subset
            if isinstance(raw, str):
                try:
                    parsed = json.loads(raw)
                except Exception:
                    parsed = None
            else:
                parsed = None
            return {
                'id': data.id,
                'product_id': data.product_id,
                'name': data.name,
                'choices': list(data.choices),
                'default_choice_id': data.default_choice_id,
                'shared_subset': parsed,
                'is_favorite': getattr(data, 'is_favorite', False) or False,
                'favorite_sort_order': getattr(data, 'favorite_sort_order', 0) or 0,
            }
        return data


# ── Products ──────────────────────────────────────────────────────────────────

class ProductBase(BaseModel):
    name: str
    category_id: Optional[int] = None
    base_price: float
    is_available: bool = True
    lifecycle_status: str = "active"
    printer_zone_id: Optional[int] = None
    sort_order: int = 0


class ProductCreate(ProductBase):
    quick_options: List[ProductQuickOptionCreate] = []
    options: List[ProductOptionCreate] = []
    ingredients: List[ProductIngredientCreate] = []
    preference_sets: List[PreferenceSetCreate] = []


class ProductUpdate(BaseModel):
    name: Optional[str] = None
    category_id: Optional[int] = None
    base_price: Optional[float] = None
    is_available: Optional[bool] = None
    lifecycle_status: Optional[str] = None
    printer_zone_id: Optional[int] = None
    sort_order: Optional[int] = None
    quick_options: Optional[List[ProductQuickOptionCreate]] = None
    options: Optional[List[ProductOptionCreate]] = None
    ingredients: Optional[List[ProductIngredientCreate]] = None
    preference_sets: Optional[List[PreferenceSetCreate]] = None


class ProductReorderItem(BaseModel):
    id: int
    sort_order: int


class ProductOut(ProductBase):
    id: int
    quick_options: List[ProductQuickOptionOut] = []
    options: List[ProductOptionOut] = []
    ingredients: List[ProductIngredientOut] = []
    preference_sets: List[PreferenceSetOut] = []
    image_url: Optional[str] = None

    model_config = {"from_attributes": True}
