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 CategoryReparentRequest(BaseModel):
    parent_id: Optional[int] = None


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


# ── Modifier Groups ───────────────────────────────────────────────────────────

class ModifierGroupCreate(BaseModel):
    modifier_type: str  # "option" | "ingredient" | "preference"
    name: str
    sort_order: int = 0


class ModifierGroupOut(BaseModel):
    id: int
    product_id: int
    modifier_type: str
    name: str
    sort_order: int = 0

    model_config = {"from_attributes": True}


# ── 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
    # When True waiter can add more than 1 of this sub-choice
    allow_multiple: bool = False
    # When True renders as half-width card on the PWA
    is_compact: bool = False


class ProductOptionBase(BaseModel):
    name: str
    extra_cost: float = 0.0
    allow_multiple: bool = False
    # When True waiter can select more than one sub-choice at once
    multi_select: bool = False
    is_favorite: bool = False
    favorite_sort_order: int = 0
    # When True renders the option itself as half-width card on the PWA
    is_compact: bool = False
    group_id: Optional[int] = None


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,
                'multi_select': getattr(data, 'multi_select', 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,
                'is_compact': getattr(data, 'is_compact', False) or False,
                'group_id': getattr(data, 'group_id', None),
            }
        return data


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

class ProductIngredientBase(BaseModel):
    name: str
    extra_cost: float = 0.0
    is_favorite: bool = False
    favorite_sort_order: int = 0
    # When True renders as half-width card on the PWA
    is_compact: bool = False
    group_id: Optional[int] = None


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
    is_compact: 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
    is_compact: 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
    # When True renders as half-width card on the PWA
    is_compact: 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
    is_compact: 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,
                'is_compact': getattr(data, 'is_compact', False) 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
    group_id: Optional[int] = None
    allow_multi_select: bool = False
    allow_choice_quantity: bool = False


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
    group_id: Optional[int] = None
    allow_multi_select: bool = False
    allow_choice_quantity: bool = False

    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,
                'group_id': getattr(data, 'group_id', None),
                'allow_multi_select': getattr(data, 'allow_multi_select', False) or False,
                'allow_choice_quantity': getattr(data, 'allow_choice_quantity', False) or False,
            }
        return data


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

class CostBreakdownItem(BaseModel):
    label: str
    amount: float


class ProductBase(BaseModel):
    name: str
    description: Optional[str] = None
    category_id: Optional[int] = None
    base_price: float
    # piece | portion | kg | liter | gram | ml
    unit_type: str = "piece"
    is_available: bool = True
    lifecycle_status: str = "active"
    printer_zone_id: Optional[int] = None  # kept for backward compat; use prep_zone_ids instead
    prep_zone_ids: List[int] = []
    sort_order: int = 0
    # Xenia Connect — digital menu overrides
    digital_visible: bool = True
    digital_available: bool = True
    digital_name: Optional[str] = None
    digital_description: Optional[str] = None
    digital_price: Optional[float] = None
    digital_discount: Optional[float] = None
    digital_image_url: Optional[str] = None
    # Phase 2A — cost tracking
    cost_simple: Optional[float] = None
    cost_breakdown: Optional[List[CostBreakdownItem]] = None
    # Quick Add
    quick_add_enabled: bool = True
    # Tags
    tags: Optional[List[str]] = None
    # Service item flag
    is_service_item: bool = False
    # Fiscal printer (ΦΗΜ)
    fiscal_name: Optional[str] = None
    fiscal_vat_group_id: Optional[int] = None


class ProductCreate(ProductBase):
    quick_options: List[ProductQuickOptionCreate] = []
    options: List[ProductOptionCreate] = []
    ingredients: List[ProductIngredientCreate] = []
    preference_sets: List[PreferenceSetCreate] = []
    # group_id on options/ingredients/preference_sets is an index into this list
    modifier_groups: List[ModifierGroupCreate] = []


class ProductUpdate(BaseModel):
    name: Optional[str] = None
    description: Optional[str] = None
    category_id: Optional[int] = None
    base_price: Optional[float] = None
    unit_type: Optional[str] = None
    is_available: Optional[bool] = None
    lifecycle_status: Optional[str] = None
    printer_zone_id: Optional[int] = None
    prep_zone_ids: Optional[List[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
    modifier_groups: Optional[List[ModifierGroupCreate]] = None
    # Xenia Connect — digital menu overrides
    digital_visible: Optional[bool] = None
    digital_available: Optional[bool] = None
    digital_name: Optional[str] = None
    digital_description: Optional[str] = None
    digital_price: Optional[float] = None
    digital_discount: Optional[float] = None
    digital_image_url: Optional[str] = None
    # Phase 2A — cost tracking
    cost_simple: Optional[float] = None
    cost_breakdown: Optional[List[CostBreakdownItem]] = None
    # Quick Add
    quick_add_enabled: Optional[bool] = None
    # Tags
    tags: Optional[List[str]] = None
    # Service item flag
    is_service_item: Optional[bool] = None
    # Fiscal printer (ΦΗΜ)
    fiscal_name: Optional[str] = None
    fiscal_vat_group_id: Optional[int] = 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] = []
    modifier_groups: List[ModifierGroupOut] = []
    image_url: Optional[str] = None

    model_config = {"from_attributes": True}

    @model_validator(mode='before')
    @classmethod
    def parse_cost_breakdown(cls, data: Any) -> Any:
        if hasattr(data, 'cost_breakdown'):
            raw = data.cost_breakdown
            parsed = None
            if isinstance(raw, str):
                try:
                    parsed = json.loads(raw)
                except Exception:
                    parsed = None
            # Return a plain dict so other validators still see all fields
            return {
                'id': data.id,
                'name': data.name,
                'description': data.description,
                'category_id': data.category_id,
                'base_price': data.base_price,
                'unit_type': getattr(data, 'unit_type', 'piece') or 'piece',
                'is_available': data.is_available,
                'lifecycle_status': data.lifecycle_status,
                'printer_zone_id': data.printer_zone_id,
                'prep_zone_ids': [z.id for z in data.prep_zones] if hasattr(data, 'prep_zones') else [],
                'sort_order': data.sort_order,
                'image_url': data.image_url,
                'digital_visible': bool(data.digital_visible),
                'digital_available': bool(data.digital_available),
                'digital_name': data.digital_name,
                'digital_description': data.digital_description,
                'digital_price': data.digital_price,
                'digital_discount': data.digital_discount,
                'digital_image_url': data.digital_image_url,
                'cost_simple': data.cost_simple,
                'cost_breakdown': parsed,
                'quick_add_enabled': getattr(data, 'quick_add_enabled', True),
                'tags': json.loads(data.tags) if isinstance(getattr(data, 'tags', None), str) else (getattr(data, 'tags', None) or []),
                'is_service_item': bool(getattr(data, 'is_service_item', False)),
                'fiscal_name': getattr(data, 'fiscal_name', None),
                'fiscal_vat_group_id': getattr(data, 'fiscal_vat_group_id', None),
                'quick_options': list(data.quick_options),
                'options': list(data.options),
                'ingredients': list(data.ingredients),
                'preference_sets': list(data.preference_sets),
                'modifier_groups': list(data.modifier_groups) if hasattr(data, 'modifier_groups') else [],
            }
        return data
