import json
from datetime import datetime, timedelta
from typing import Literal, Optional

from redbot.core.bot import Red

from ..utilities.utils import timedelta_from_string
from .base import AuroraBaseModel
from .partials import PartialUser


class Change(AuroraBaseModel):
    type: Literal["ORIGINAL", "RESOLVE", "EDIT"]
    timestamp: datetime
    user_id: int
    reason: Optional[str] = None
    duration: Optional[timedelta] = None
    end_timestamp: Optional[datetime] = None

    @property
    def unix_timestamp(self) -> int:
        return int(self.timestamp.timestamp())

    @property
    def unix_end_timestamp(self) -> Optional[int]:
        if self.end_timestamp:
            return int(self.end_timestamp.timestamp())
        return None

    def __str__(self):
        return f"{self.type} {self.user_id} {self.reason}"

    def __repr__(self) -> str:
        attrs = [
            ('type', self.type),
            ('timestamp', self.timestamp),
            ('user_id', self.user_id),
            ('reason', self.reason),
            ('duration', self.duration),
            ('end_timestamp', self.end_timestamp),
        ]
        joined = ' '.join(f'{key}={value!r}' for key, value in attrs)
        return f"<{self.__class__.__name__} {joined}>"

    async def get_user(self) -> "PartialUser":
        return await PartialUser.from_id(self.bot, self.user_id)

    @classmethod
    def from_dict(cls, bot: Red, data: dict) -> "Change":
        if isinstance(data, str):
            data = json.loads(data)
        if data.get('duration') and not isinstance(data["duration"], timedelta) and not data["duration"] == "NULL":
            duration = timedelta_from_string(data["duration"])
        elif data.get('duration') and isinstance(data["duration"], timedelta):
            duration = data["duration"]
        else:
            duration = None

        if data.get('end_timestamp') and not isinstance(data["end_timestamp"], datetime):
            end_timestamp = datetime.fromtimestamp(data["end_timestamp"])
        elif data.get('end_timestamp') and isinstance(data["end_timestamp"], datetime):
            end_timestamp = data["end_timestamp"]
        else:
            end_timestamp = None

        if not isinstance(data["timestamp"], datetime):
            timestamp = datetime.fromtimestamp(data["timestamp"])
        else:
            timestamp = data["timestamp"]

        try:
            data["user_id"] = int(data["user_id"])
        except ValueError:
            data["user_id"] = 0

        data.update({
            "timestamp": timestamp,
            "end_timestamp": end_timestamp,
            "duration": duration
        })
        if "bot" in data:
            del data["bot"]
        return cls(bot=bot, **data)
