from enum import Enum
from typing import Annotated, Any, Dict, List, Optional
from uuid import uuid4
from pydantic import BaseModel, Field, validator
from pydantic.deprecated.class_validators import root_validator


def to_camel_case(snake_str: str) -> str:
    components = snake_str.split("_")
    return components[0] + "".join(x.title() for x in components[1:])


class ColumnItemType(str, Enum):
    PRODUCT = "PRODUCT"
    COMPONENT = "COMPONENT"


QuantityInt = Annotated[int, Field(ge=0, le=2147483647)]
StrictSmallInt = Annotated[int, Field(ge=0, le=32767)]


class CorrelationId(BaseModel):
    correlation_id: str = Field(default_factory=lambda: uuid4().hex)


class ColumnsInput(BaseModel):
    column_number: StrictSmallInt = Field(
        description="View index in Televend",
    )
    external_product_id: Optional[str] = Field(
        default=None,
        description="Product or Component external ID used for product/component identification in external partner's ERP system",
        min_length=1,
        max_length=32,
    )
    old_qty: Optional[QuantityInt] = Field(
        default_factory=lambda: None,
    )
    new_qty: Optional[QuantityInt] = Field(
        default_factory=lambda: None,
    )

    old_price: Optional[float] = Field(
        default_factory=lambda: None,
        alias="oldPrc",
        ge=0,
        le=99999999.99,
    )
    new_price: Optional[float] = Field(
        default_factory=lambda: None,
        alias="newPrc",
        ge=0,
        le=99999999.99,
    )

    select_map: Optional[List[StrictSmallInt]] = Field(default_factory=lambda: None)

    item_type: Optional[ColumnItemType] = Field(
        default_factory=lambda: ColumnItemType.PRODUCT,
        description="MUST be set if item is COMPONENT",
    )

    class Config:
        populate_by_name = True
        alias_generator = to_camel_case
        str_strip_whitespace = True

    @validator("item_type")
    def set_item_type(cls, value):
        return value or ColumnItemType.PRODUCT


class PlanogramInput(CorrelationId, BaseModel):
    machine_external_id: str = Field(
        description="Machine external ID",
        min_length=1,
        max_length=32,
    )
    columns: List[ColumnsInput] = Field(
        title="Columns", description="A list of columns this planogram specifies"
    )

    class Config:
        title = "Planogram"
        alias_generator = to_camel_case
        populate_by_name = True
        str_strip_whitespace = True


class PlanogramsBulkInputPayload(BaseModel):
    planograms: List[PlanogramInput] = Field(
        title="Planograms",
        description="A list of Planograms this bulk carries",
    )

    class Config:
        populate_by_name = True
        alias_generator = to_camel_case
