from __future__ import annotations

import datetime
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 dateutil.parser import isoparse

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

if TYPE_CHECKING:
    from ..models.keyword import Keyword
    from ..models.nutrition_information import NutritionInformation
    from ..models.property_ import Property
    from ..models.step import Step
    from ..models.user import User


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


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

    Attributes:
        id (int | Unset):
        name (str | Unset):
        description (None | str | Unset):
        image (None | str | Unset):
        keywords (list[Keyword] | Unset):
        steps (list[Step] | Unset):
        working_time (int | Unset):
        waiting_time (int | Unset):
        created_by (User | Unset): Adds nested create feature
        created_at (datetime.datetime | Unset):
        updated_at (datetime.datetime | Unset):
        source_url (None | str | Unset):
        internal (bool | Unset):
        show_ingredient_overview (bool | Unset):
        nutrition (None | NutritionInformation | Unset):
        properties (list[Property] | Unset):
        food_properties (Any | Unset):
        servings (int | Unset):
        file_path (str | Unset):
        servings_text (str | Unset):
        diameter (int | Unset):
        diameter_text (str | Unset):
        rating (float | None | Unset):
        last_cooked (datetime.datetime | None | Unset):
        private (bool | Unset):
        shared (list[User] | Unset):
    """

    id: int | Unset = UNSET
    name: str | Unset = UNSET
    description: None | str | Unset = UNSET
    image: None | str | Unset = UNSET
    keywords: list[Keyword] | Unset = UNSET
    steps: list[Step] | Unset = UNSET
    working_time: int | Unset = UNSET
    waiting_time: int | Unset = UNSET
    created_by: User | Unset = UNSET
    created_at: datetime.datetime | Unset = UNSET
    updated_at: datetime.datetime | Unset = UNSET
    source_url: None | str | Unset = UNSET
    internal: bool | Unset = UNSET
    show_ingredient_overview: bool | Unset = UNSET
    nutrition: None | NutritionInformation | Unset = UNSET
    properties: list[Property] | Unset = UNSET
    food_properties: Any | Unset = UNSET
    servings: int | Unset = UNSET
    file_path: str | Unset = UNSET
    servings_text: str | Unset = UNSET
    diameter: int | Unset = UNSET
    diameter_text: str | Unset = UNSET
    rating: float | None | Unset = UNSET
    last_cooked: datetime.datetime | None | Unset = UNSET
    private: bool | Unset = UNSET
    shared: list[User] | Unset = UNSET
    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)

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

        id = self.id

        name = self.name

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

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

        keywords: list[dict[str, Any]] | Unset = UNSET
        if not isinstance(self.keywords, Unset):
            keywords = []
            for keywords_item_data in self.keywords:
                keywords_item = keywords_item_data.to_dict()
                keywords.append(keywords_item)

        steps: list[dict[str, Any]] | Unset = UNSET
        if not isinstance(self.steps, Unset):
            steps = []
            for steps_item_data in self.steps:
                steps_item = steps_item_data.to_dict()
                steps.append(steps_item)

        working_time = self.working_time

        waiting_time = self.waiting_time

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

        created_at: str | Unset = UNSET
        if not isinstance(self.created_at, Unset):
            created_at = self.created_at.isoformat()

        updated_at: str | Unset = UNSET
        if not isinstance(self.updated_at, Unset):
            updated_at = self.updated_at.isoformat()

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

        internal = self.internal

        show_ingredient_overview = self.show_ingredient_overview

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

        properties: list[dict[str, Any]] | Unset = UNSET
        if not isinstance(self.properties, Unset):
            properties = []
            for properties_item_data in self.properties:
                properties_item = properties_item_data.to_dict()
                properties.append(properties_item)

        food_properties = self.food_properties

        servings = self.servings

        file_path = self.file_path

        servings_text = self.servings_text

        diameter = self.diameter

        diameter_text = self.diameter_text

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

        last_cooked: None | str | Unset
        if isinstance(self.last_cooked, Unset):
            last_cooked = UNSET
        elif isinstance(self.last_cooked, datetime.datetime):
            last_cooked = self.last_cooked.isoformat()
        else:
            last_cooked = self.last_cooked

        private = self.private

        shared: list[dict[str, Any]] | Unset = UNSET
        if not isinstance(self.shared, Unset):
            shared = []
            for shared_item_data in self.shared:
                shared_item = shared_item_data.to_dict()
                shared.append(shared_item)

        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 description is not UNSET:
            field_dict["description"] = description
        if image is not UNSET:
            field_dict["image"] = image
        if keywords is not UNSET:
            field_dict["keywords"] = keywords
        if steps is not UNSET:
            field_dict["steps"] = steps
        if working_time is not UNSET:
            field_dict["working_time"] = working_time
        if waiting_time is not UNSET:
            field_dict["waiting_time"] = waiting_time
        if created_by is not UNSET:
            field_dict["created_by"] = created_by
        if created_at is not UNSET:
            field_dict["created_at"] = created_at
        if updated_at is not UNSET:
            field_dict["updated_at"] = updated_at
        if source_url is not UNSET:
            field_dict["source_url"] = source_url
        if internal is not UNSET:
            field_dict["internal"] = internal
        if show_ingredient_overview is not UNSET:
            field_dict["show_ingredient_overview"] = show_ingredient_overview
        if nutrition is not UNSET:
            field_dict["nutrition"] = nutrition
        if properties is not UNSET:
            field_dict["properties"] = properties
        if food_properties is not UNSET:
            field_dict["food_properties"] = food_properties
        if servings is not UNSET:
            field_dict["servings"] = servings
        if file_path is not UNSET:
            field_dict["file_path"] = file_path
        if servings_text is not UNSET:
            field_dict["servings_text"] = servings_text
        if diameter is not UNSET:
            field_dict["diameter"] = diameter
        if diameter_text is not UNSET:
            field_dict["diameter_text"] = diameter_text
        if rating is not UNSET:
            field_dict["rating"] = rating
        if last_cooked is not UNSET:
            field_dict["last_cooked"] = last_cooked
        if private is not UNSET:
            field_dict["private"] = private
        if shared is not UNSET:
            field_dict["shared"] = shared

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        if not isinstance(self.shared, Unset):
            for shared_item_element in self.shared:
                files.append(("shared", (None, json.dumps(shared_item_element.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.keyword import Keyword
        from ..models.nutrition_information import NutritionInformation
        from ..models.property_ import Property
        from ..models.step import Step
        from ..models.user import User

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

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

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

        description = _parse_description(d.pop("description", UNSET))

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

        image = _parse_image(d.pop("image", UNSET))

        _keywords = d.pop("keywords", UNSET)
        keywords: list[Keyword] | Unset = UNSET
        if _keywords is not UNSET:
            keywords = []
            for keywords_item_data in _keywords:
                keywords_item = Keyword.from_dict(keywords_item_data)

                keywords.append(keywords_item)

        _steps = d.pop("steps", UNSET)
        steps: list[Step] | Unset = UNSET
        if _steps is not UNSET:
            steps = []
            for steps_item_data in _steps:
                steps_item = Step.from_dict(steps_item_data)

                steps.append(steps_item)

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

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

        _created_by = d.pop("created_by", UNSET)
        created_by: User | Unset
        if isinstance(_created_by, Unset):
            created_by = UNSET
        else:
            created_by = User.from_dict(_created_by)

        _created_at = d.pop("created_at", UNSET)
        created_at: datetime.datetime | Unset
        if isinstance(_created_at, Unset):
            created_at = UNSET
        else:
            created_at = isoparse(_created_at)

        _updated_at = d.pop("updated_at", UNSET)
        updated_at: datetime.datetime | Unset
        if isinstance(_updated_at, Unset):
            updated_at = UNSET
        else:
            updated_at = isoparse(_updated_at)

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

        source_url = _parse_source_url(d.pop("source_url", UNSET))

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

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

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

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

        nutrition = _parse_nutrition(d.pop("nutrition", UNSET))

        _properties = d.pop("properties", UNSET)
        properties: list[Property] | Unset = UNSET
        if _properties is not UNSET:
            properties = []
            for properties_item_data in _properties:
                properties_item = Property.from_dict(properties_item_data)

                properties.append(properties_item)

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

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

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

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

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

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

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

        rating = _parse_rating(d.pop("rating", UNSET))

        def _parse_last_cooked(data: object) -> datetime.datetime | None | Unset:
            if data is None:
                return data
            if isinstance(data, Unset):
                return data
            try:
                if not isinstance(data, str):
                    raise TypeError()
                last_cooked_type_0 = isoparse(data)

                return last_cooked_type_0
            except (TypeError, ValueError, AttributeError, KeyError):
                pass
            return cast(datetime.datetime | None | Unset, data)

        last_cooked = _parse_last_cooked(d.pop("last_cooked", UNSET))

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

        _shared = d.pop("shared", UNSET)
        shared: list[User] | Unset = UNSET
        if _shared is not UNSET:
            shared = []
            for shared_item_data in _shared:
                shared_item = User.from_dict(shared_item_data)

                shared.append(shared_item)

        patched_recipe = cls(
            id=id,
            name=name,
            description=description,
            image=image,
            keywords=keywords,
            steps=steps,
            working_time=working_time,
            waiting_time=waiting_time,
            created_by=created_by,
            created_at=created_at,
            updated_at=updated_at,
            source_url=source_url,
            internal=internal,
            show_ingredient_overview=show_ingredient_overview,
            nutrition=nutrition,
            properties=properties,
            food_properties=food_properties,
            servings=servings,
            file_path=file_path,
            servings_text=servings_text,
            diameter=diameter,
            diameter_text=diameter_text,
            rating=rating,
            last_cooked=last_cooked,
            private=private,
            shared=shared,
        )

        patched_recipe.additional_properties = d
        return patched_recipe

    @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
