from datetime import datetime
from uuid import UUID
from typing import Union, Optional, List
from dateutil.parser import parse
from datalogue.errors import DtlError


class DataProduct:

    def __init__(
            self,
            name: str,
            description: Optional[str] = None,
            pipeline_ids: List[UUID] = [],
            id: Optional[UUID] = None,
            owner: Optional[UUID] = None,
            created_by: Optional[UUID] = None,
            created_at: Optional[datetime] = None,
            last_updated_by: Optional[UUID] = None,
            last_updated_at: Optional[datetime] = None
    ):
        self.id = id
        self.name = name
        self.description = description
        self.owner = owner
        self.created_by = created_by
        self.created_at = created_at
        self.last_updated_by = last_updated_by
        self.last_updated_at = last_updated_at
        self.pipeline_ids = pipeline_ids
    def __eq__(self, other):
        return (
                (self.id == other.id)
                and (self.name == other.name)
                and (self.created_at == other.created_at)
                and (self.created_by == other.created_by)
        )

    def __repr__(self):
        return (
            f"{self.__class__.__name__!r}(\n "
            f"id= {self.id!r},\n "
            f"name= {self.name!r},\n "
            f"description= {self.description!r},\n "
            f"pipeline_ids= {self.pipeline_ids!r},\n "
            f"owner= {self.owner!r},\n "
            f"created_by= {self.created_by!r},\n "
            f"created_at= {self.created_at!r},\n "
            f"last_updated_by= {self.last_updated_by!r},\n "
            f"last_updated_at= {self.last_updated_at!r}\n "
            ")"
        )

    def _as_payload(self) -> dict:
        payload = {"name": self.name}
        if self.description is not None:
            payload["description"] = self.description
        payload["streamIds"] = self.pipeline_ids
        return payload

    @classmethod
    def _from_payload(cls, json: dict) -> Union[DtlError, "DataProduct"]:
        """
        Parses a dictionary into a Data Product object.
        """
        id = json.get("id")
        if id is None:
            return DtlError("Data Product object should have a 'id' property")
        else:
            try:
                id = UUID(id)
            except ValueError:
                return DtlError("'id' field was not a proper uuid")

        name = json.get("name")
        if name is None:
            return DtlError("Data Product object should have a 'name' property")

        description = json.get("description")
        owner = json.get("owner")
        if owner is None:
            return DtlError("Data Product object should have a 'owner' property")
        else:
            try:
                owner = UUID(owner)
            except ValueError:
                return DtlError("'owner' field was not a proper uuid")

        created_by = json.get("createdBy")
        if created_by is None:
            return DtlError("Data Product object should have a 'createdBy' property")
        else:
            try:
                created_by = UUID(created_by)
            except ValueError:
                return DtlError("'createdBy' field was not a proper uuid")

        created_at = json.get("createdAt")
        if created_at is None:
            return DtlError("Data Product object should have a 'createdAt' property")
        else:
            try:
                created_at = parse(created_at)
            except ValueError:
                return DtlError("The 'createdAt' could not be parsed as a valid date")

        last_updated_by = json.get("lastUpdatedBy")
        if last_updated_by is None:
            return DtlError("Data Product object should have a 'lastUpdatedBy' property")
        else:
            try:
                last_updated_by = UUID(last_updated_by)
            except ValueError:
                return DtlError("'lastUpdatedBy' field was not a proper uuid")

        last_updated_at = json.get("lastUpdatedAt")
        if last_updated_at is None:
            return DtlError("Data Product object should have a 'lastUpdatedAt' property")
        else:
            try:
                last_updated_at = parse(last_updated_at)
            except ValueError:
                return DtlError("The 'lastUpdatedAt' could not be parsed as a valid date")

        pipeline_ids = json.get("streamIds")
        if pipeline_ids is None:
            return DtlError("Data Product object should have a 'streamIds' property")

        return cls(
            name=name,
            id=id,
            description=description,
            owner=owner,
            created_by=created_by,
            created_at=created_at,
            last_updated_by=last_updated_by,
            last_updated_at=last_updated_at,
            pipeline_ids=pipeline_ids
        )