from __future__ import annotations

import json
from collections.abc import Mapping
from typing import TYPE_CHECKING, Any, TypeVar, cast

from attrs import define as _attrs_define
from attrs import field as _attrs_field

from .. import types
from ..types import UNSET, Unset

if TYPE_CHECKING:
    from ..models.food import Food
    from ..models.unit import Unit


T = TypeVar("T", bound="PatchedUnitConversion")


@_attrs_define
class PatchedUnitConversion:
    """Adds nested create feature

    Attributes:
        id (int | Unset):
        name (str | Unset):
        base_amount (float | Unset):
        base_unit (Unit | Unset): Moves `UniqueValidator`'s from the validation stage to the save stage.
            It solves the problem with nested validation for unique fields on update.

            If you want more details, you can read related issues and articles:
            https://github.com/beda-software/drf-writable-nested/issues/1
            http://www.django-rest-framework.org/api-guide/validators/#updating-nested-serializers

            Example of usage:
            ```
                class Child(models.Model):
                field = models.CharField(unique=True)


            class Parent(models.Model):
                child = models.ForeignKey('Child')


            class ChildSerializer(UniqueFieldsMixin, serializers.ModelSerializer):
                class Meta:
                    model = Child


            class ParentSerializer(NestedUpdateMixin, serializers.ModelSerializer):
                child = ChildSerializer()

                class Meta:
                    model = Parent
            ```

            Note: `UniqueFieldsMixin` must be applied only on the serializer
            which has unique fields.

            Note: When you are using both mixins
            (`UniqueFieldsMixin` and `NestedCreateMixin` or `NestedUpdateMixin`)
            you should put `UniqueFieldsMixin` ahead.
        converted_amount (float | Unset):
        converted_unit (Unit | Unset): Moves `UniqueValidator`'s from the validation stage to the save stage.
            It solves the problem with nested validation for unique fields on update.

            If you want more details, you can read related issues and articles:
            https://github.com/beda-software/drf-writable-nested/issues/1
            http://www.django-rest-framework.org/api-guide/validators/#updating-nested-serializers

            Example of usage:
            ```
                class Child(models.Model):
                field = models.CharField(unique=True)


            class Parent(models.Model):
                child = models.ForeignKey('Child')


            class ChildSerializer(UniqueFieldsMixin, serializers.ModelSerializer):
                class Meta:
                    model = Child


            class ParentSerializer(NestedUpdateMixin, serializers.ModelSerializer):
                child = ChildSerializer()

                class Meta:
                    model = Parent
            ```

            Note: `UniqueFieldsMixin` must be applied only on the serializer
            which has unique fields.

            Note: When you are using both mixins
            (`UniqueFieldsMixin` and `NestedCreateMixin` or `NestedUpdateMixin`)
            you should put `UniqueFieldsMixin` ahead.
        food (Food | None | Unset):
        open_data_slug (None | str | Unset):
    """

    id: int | Unset = UNSET
    name: str | Unset = UNSET
    base_amount: float | Unset = UNSET
    base_unit: Unit | Unset = UNSET
    converted_amount: float | Unset = UNSET
    converted_unit: Unit | Unset = UNSET
    food: Food | None | Unset = UNSET
    open_data_slug: None | str | Unset = UNSET
    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)

    def to_dict(self) -> dict[str, Any]:
        from ..models.food import Food

        id = self.id

        name = self.name

        base_amount = self.base_amount

        base_unit: dict[str, Any] | Unset = UNSET
        if not isinstance(self.base_unit, Unset):
            base_unit = self.base_unit.to_dict()

        converted_amount = self.converted_amount

        converted_unit: dict[str, Any] | Unset = UNSET
        if not isinstance(self.converted_unit, Unset):
            converted_unit = self.converted_unit.to_dict()

        food: dict[str, Any] | None | Unset
        if isinstance(self.food, Unset):
            food = UNSET
        elif isinstance(self.food, Food):
            food = self.food.to_dict()
        else:
            food = self.food

        open_data_slug: None | str | Unset
        if isinstance(self.open_data_slug, Unset):
            open_data_slug = UNSET
        else:
            open_data_slug = self.open_data_slug

        field_dict: dict[str, Any] = {}
        field_dict.update(self.additional_properties)
        field_dict.update({})
        if id is not UNSET:
            field_dict["id"] = id
        if name is not UNSET:
            field_dict["name"] = name
        if base_amount is not UNSET:
            field_dict["base_amount"] = base_amount
        if base_unit is not UNSET:
            field_dict["base_unit"] = base_unit
        if converted_amount is not UNSET:
            field_dict["converted_amount"] = converted_amount
        if converted_unit is not UNSET:
            field_dict["converted_unit"] = converted_unit
        if food is not UNSET:
            field_dict["food"] = food
        if open_data_slug is not UNSET:
            field_dict["open_data_slug"] = open_data_slug

        return field_dict

    def to_multipart(self) -> types.RequestFiles:
        files: types.RequestFiles = []

        if not isinstance(self.id, Unset):
            files.append(("id", (None, str(self.id).encode(), "text/plain")))

        if not isinstance(self.name, Unset):
            files.append(("name", (None, str(self.name).encode(), "text/plain")))

        if not isinstance(self.base_amount, Unset):
            files.append(("base_amount", (None, str(self.base_amount).encode(), "text/plain")))

        if not isinstance(self.base_unit, Unset):
            files.append(("base_unit", (None, json.dumps(self.base_unit.to_dict()).encode(), "application/json")))

        if not isinstance(self.converted_amount, Unset):
            files.append(("converted_amount", (None, str(self.converted_amount).encode(), "text/plain")))

        if not isinstance(self.converted_unit, Unset):
            files.append(
                ("converted_unit", (None, json.dumps(self.converted_unit.to_dict()).encode(), "application/json"))
            )

        if not isinstance(self.food, Unset):
            if self.food is None:
                files.append(("food", (None, str(self.food).encode(), "text/plain")))
            else:
                files.append(("food", (None, json.dumps(self.food.to_dict()).encode(), "application/json")))

        if not isinstance(self.open_data_slug, Unset):
            if isinstance(self.open_data_slug, str):
                files.append(("open_data_slug", (None, str(self.open_data_slug).encode(), "text/plain")))
            else:
                files.append(("open_data_slug", (None, str(self.open_data_slug).encode(), "text/plain")))

        for prop_name, prop in self.additional_properties.items():
            files.append((prop_name, (None, str(prop).encode(), "text/plain")))

        return files

    @classmethod
    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
        from ..models.food import Food
        from ..models.unit import Unit

        d = dict(src_dict)
        id = d.pop("id", UNSET)

        name = d.pop("name", UNSET)

        base_amount = d.pop("base_amount", UNSET)

        _base_unit = d.pop("base_unit", UNSET)
        base_unit: Unit | Unset
        if isinstance(_base_unit, Unset):
            base_unit = UNSET
        else:
            base_unit = Unit.from_dict(_base_unit)

        converted_amount = d.pop("converted_amount", UNSET)

        _converted_unit = d.pop("converted_unit", UNSET)
        converted_unit: Unit | Unset
        if isinstance(_converted_unit, Unset):
            converted_unit = UNSET
        else:
            converted_unit = Unit.from_dict(_converted_unit)

        def _parse_food(data: object) -> Food | None | Unset:
            if data is None:
                return data
            if isinstance(data, Unset):
                return data
            try:
                if not isinstance(data, dict):
                    raise TypeError()
                food_type_1 = Food.from_dict(data)

                return food_type_1
            except (TypeError, ValueError, AttributeError, KeyError):
                pass
            return cast(Food | None | Unset, data)

        food = _parse_food(d.pop("food", UNSET))

        def _parse_open_data_slug(data: object) -> None | str | Unset:
            if data is None:
                return data
            if isinstance(data, Unset):
                return data
            return cast(None | str | Unset, data)

        open_data_slug = _parse_open_data_slug(d.pop("open_data_slug", UNSET))

        patched_unit_conversion = cls(
            id=id,
            name=name,
            base_amount=base_amount,
            base_unit=base_unit,
            converted_amount=converted_amount,
            converted_unit=converted_unit,
            food=food,
            open_data_slug=open_data_slug,
        )

        patched_unit_conversion.additional_properties = d
        return patched_unit_conversion

    @property
    def additional_keys(self) -> list[str]:
        return list(self.additional_properties.keys())

    def __getitem__(self, key: str) -> Any:
        return self.additional_properties[key]

    def __setitem__(self, key: str, value: Any) -> None:
        self.additional_properties[key] = value

    def __delitem__(self, key: str) -> None:
        del self.additional_properties[key]

    def __contains__(self, key: str) -> bool:
        return key in self.additional_properties
