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.property_type import PropertyType


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


@_attrs_define
class PatchedProperty:
    """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.

        Attributes:
            id (int | Unset):
            property_amount (float | None | Unset):
            property_type (PropertyType | Unset): Adds nested create feature
    """

    id: int | Unset = UNSET
    property_amount: float | None | Unset = UNSET
    property_type: PropertyType | Unset = UNSET
    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)

    def to_dict(self) -> dict[str, Any]:
        id = self.id

        property_amount: float | None | Unset
        if isinstance(self.property_amount, Unset):
            property_amount = UNSET
        else:
            property_amount = self.property_amount

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

        field_dict: dict[str, Any] = {}
        field_dict.update(self.additional_properties)
        field_dict.update({})
        if id is not UNSET:
            field_dict["id"] = id
        if property_amount is not UNSET:
            field_dict["property_amount"] = property_amount
        if property_type is not UNSET:
            field_dict["property_type"] = property_type

        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.property_amount, Unset):
            if isinstance(self.property_amount, float):
                files.append(("property_amount", (None, str(self.property_amount).encode(), "text/plain")))
            else:
                files.append(("property_amount", (None, str(self.property_amount).encode(), "text/plain")))

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

        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.property_type import PropertyType

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

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

        property_amount = _parse_property_amount(d.pop("property_amount", UNSET))

        _property_type = d.pop("property_type", UNSET)
        property_type: PropertyType | Unset
        if isinstance(_property_type, Unset):
            property_type = UNSET
        else:
            property_type = PropertyType.from_dict(_property_type)

        patched_property = cls(
            id=id,
            property_amount=property_amount,
            property_type=property_type,
        )

        patched_property.additional_properties = d
        return patched_property

    @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
