from pydantic import AliasChoices, BaseModel, ConfigDict, Field, field_validator


class LoginRequest(BaseModel):
    identifier: str | None = None
    password: str | None = None
    session_token: str | None = None


class ProductUpsert(BaseModel):
    model_config = ConfigDict(populate_by_name=True)

    product_name: str = Field(validation_alias=AliasChoices("product_name", "prodotto", "Prodotto"))
    lot_code: str = Field(validation_alias=AliasChoices("lot_code", "lotto", "LOTTO"))
    supplier_name: str = Field(validation_alias=AliasChoices("supplier_name", "fornitore", "FORNITORE"))
    product_code: str | None = Field(default=None, validation_alias=AliasChoices("product_code", "codice_prodotto", "codice"))
    final_price_vat: float | None = Field(
        default=None,
        validation_alias=AliasChoices("final_price_vat", "prezzo_finale_ivato", "prezzo"),
    )
    vat_rate: float | None = Field(default=None, validation_alias=AliasChoices("vat_rate", "iva"))
    weight_kg: float | None = Field(default=None, validation_alias=AliasChoices("weight_kg", "um", "peso_kg", "peso"))
    unit_price_per_kg: float | None = Field(
        default=None,
        validation_alias=AliasChoices("unit_price_per_kg", "prezzo_un_kg", "prz_un_kg"),
    )
    category: str | None = Field(default=None, validation_alias=AliasChoices("category", "categoria"))
    notes: str | None = Field(default=None, validation_alias=AliasChoices("notes", "note"))
    units_per_pack: float | None = Field(default=None, validation_alias=AliasChoices("units_per_pack", "unit_per_pack"))
    liters_per_unit: float | None = None

    @field_validator("product_name", "lot_code", "supplier_name", mode="before")
    @classmethod
    def _strip_required(cls, value: object) -> str:
        return str(value or "").strip()

    @field_validator("product_code", "category", "notes", mode="before")
    @classmethod
    def _strip_optional_text(cls, value: object) -> str | None:
        if value is None:
            return None
        text = str(value).strip()
        return text or None

    @field_validator(
        "final_price_vat",
        "vat_rate",
        "weight_kg",
        "unit_price_per_kg",
        "units_per_pack",
        "liters_per_unit",
        mode="before",
    )
    @classmethod
    def _coerce_optional_float(cls, value: object) -> float | None:
        if value is None:
            return None
        if isinstance(value, (int, float)):
            return float(value)

        text = str(value).strip()
        if not text or text.upper() in {"PZ", "#DIV/0!"}:
            return None

        if text.endswith("%"):
            text = text[:-1].strip()

        if "," in text and "." in text:
            text = text.replace(".", "").replace(",", ".")
        elif "," in text:
            text = text.replace(",", ".")

        try:
            return float(text)
        except ValueError:
            return None


class ProductRead(BaseModel):
    id: int
    product_name: str
    lot_code: str
    supplier_name: str
    product_code: str | None
    final_price_vat: float | None
    vat_rate: float | None
    weight_kg: float | None
    unit_price_per_kg: float | None
    category: str | None
    notes: str | None
    units_per_pack: float | None
    liters_per_unit: float | None


class SupplierCatalogRow(BaseModel):
    model_config = ConfigDict(populate_by_name=True)

    source_name: str = Field(validation_alias=AliasChoices("source_name", "product_name", "prodotto", "nome"))
    source_lot_code: str | None = Field(
        default=None,
        validation_alias=AliasChoices("source_lot_code", "lot_code", "lotto", "formato"),
    )
    source_supplier_name: str | None = Field(
        default=None,
        validation_alias=AliasChoices("source_supplier_name", "supplier_name", "fornitore"),
    )
    product_code: str | None = Field(default=None, validation_alias=AliasChoices("product_code", "codice_prodotto", "codice"))
    final_price_vat: float | None = Field(
        default=None,
        validation_alias=AliasChoices("final_price_vat", "prezzo_finale_ivato", "prezzo"),
    )
    vat_rate: float | None = Field(default=None, validation_alias=AliasChoices("vat_rate", "iva"))
    weight_kg: float | None = Field(default=None, validation_alias=AliasChoices("weight_kg", "um", "peso_kg", "peso"))
    unit_price_per_kg: float | None = Field(
        default=None,
        validation_alias=AliasChoices("unit_price_per_kg", "prezzo_un_kg", "prz_un_kg"),
    )
    category: str | None = Field(default=None, validation_alias=AliasChoices("category", "categoria"))
    notes: str | None = Field(default=None, validation_alias=AliasChoices("notes", "note"))
    units_per_pack: float | None = Field(default=None, validation_alias=AliasChoices("units_per_pack", "unit_per_pack"))
    liters_per_unit: float | None = None

    @field_validator("source_name", mode="before")
    @classmethod
    def _strip_source_name(cls, value: object) -> str:
        return str(value or "").strip()

    @field_validator("source_lot_code", "source_supplier_name", "product_code", "category", "notes", mode="before")
    @classmethod
    def _strip_optional_text_for_catalog(cls, value: object) -> str | None:
        if value is None:
            return None
        text = str(value).strip()
        return text or None

    @field_validator(
        "final_price_vat",
        "vat_rate",
        "weight_kg",
        "unit_price_per_kg",
        "units_per_pack",
        "liters_per_unit",
        mode="before",
    )
    @classmethod
    def _coerce_optional_float_for_catalog(cls, value: object) -> float | None:
        if value is None:
            return None
        if isinstance(value, (int, float)):
            return float(value)

        text = str(value).strip()
        if not text or text.upper() in {"PZ", "#DIV/0!"}:
            return None

        if text.endswith("%"):
            text = text[:-1].strip()

        if "," in text and "." in text:
            text = text.replace(".", "").replace(",", ".")
        elif "," in text:
            text = text.replace(",", ".")

        try:
            return float(text)
        except ValueError:
            return None


class SupplierCatalogPreviewRequest(BaseModel):
    supplier_name: str | None = None
    default_lot_code: str | None = None
    create_missing: bool = True
    rows: list[SupplierCatalogRow]

    @field_validator("supplier_name", "default_lot_code", mode="before")
    @classmethod
    def _strip_preview_option(cls, value: object) -> str | None:
        if value is None:
            return None
        text = str(value).strip()
        return text or None


class SupplierCatalogCreateRequest(BaseModel):
    catalog_name: str | None = None
    source_file_name: str | None = None
    supplier_name: str | None = None
    default_lot_code: str | None = None
    rows: list[SupplierCatalogRow]

    @field_validator("catalog_name", "source_file_name", "supplier_name", "default_lot_code", mode="before")
    @classmethod
    def _strip_catalog_create_option(cls, value: object) -> str | None:
        if value is None:
            return None
        text = str(value).strip()
        return text or None


class OrderSelection(BaseModel):
    product_id: int
    quantity: int = Field(ge=1)


class OrderCreateRequest(BaseModel):
    staff: str | None = None
    items: list[OrderSelection]


class OrderUpdateRequest(BaseModel):
    batch_id: int
    staff: str | None = None
    items: list[OrderSelection]


class OrderDeleteRequest(BaseModel):
    batch_id: int | None = None
    timestamp: str | None = None


class SharedNoteCreate(BaseModel):
    text: str = Field(min_length=1, validation_alias=AliasChoices("text", "testo"))


class SafetyStockSettingsUpdate(BaseModel):
    minimum_days: float = Field(ge=0, le=365)


class GoalUpsert(BaseModel):
    model_config = ConfigDict(populate_by_name=True)

    year: int | None = None
    name: str = Field(min_length=1, validation_alias=AliasChoices("name", "nome"))
    goal_type: str = Field(validation_alias=AliasChoices("goal_type", "type", "tipo"))
    description: str | None = Field(default=None, validation_alias=AliasChoices("description", "descrizione"))
    product_match: str | None = Field(default=None, validation_alias=AliasChoices("product_match", "prodotto"))
    secondary_product_match: str | None = Field(
        default=None,
        validation_alias=AliasChoices("secondary_product_match", "secondo_prodotto"),
    )
    supplier_match: str | None = Field(default=None, validation_alias=AliasChoices("supplier_match", "fornitore"))
    target: float | None = None
    secondary_target: float | None = None
    unit_label: str | None = Field(default=None, validation_alias=AliasChoices("unit_label", "unit", "unita"))
    bonus_label: str | None = Field(default=None, validation_alias=AliasChoices("bonus_label", "bonus"))

    @field_validator("name", "goal_type", mode="before")
    @classmethod
    def _strip_goal_required(cls, value: object) -> str:
        return str(value or "").strip()

    @field_validator(
        "description",
        "product_match",
        "secondary_product_match",
        "supplier_match",
        "unit_label",
        "bonus_label",
        mode="before",
    )
    @classmethod
    def _strip_goal_optional_text(cls, value: object) -> str | None:
        if value is None:
            return None
        text = str(value).strip()
        return text or None

    @field_validator("target", "secondary_target", mode="before")
    @classmethod
    def _coerce_goal_float(cls, value: object) -> float | None:
        if value is None:
            return None
        if isinstance(value, (int, float)):
            return float(value)
        text = str(value).strip()
        if not text:
            return None
        if "," in text and "." in text:
            text = text.replace(".", "").replace(",", ".")
        elif "," in text:
            text = text.replace(",", ".")
        try:
            return float(text)
        except ValueError:
            return None
