from datetime import timedelta

from django.db import models
from django.db.models.functions import Lower
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from loguru import logger
from typedmodels.models import TypedModel

from catalog.common import jsondata


class Platform(models.TextChoices):
    EMAIL = "email", _("Email")
    MASTODON = "mastodon", _("Mastodon")
    THREADS = "threads", _("Threads")
    BLUESKY = "bluesky", _("Bluesky")


class SocialAccount(TypedModel):
    user = models.ForeignKey(
        "users.User",
        on_delete=models.CASCADE,
        related_name="social_accounts",
        null=True,
    )
    domain = models.CharField(max_length=255, null=False, blank=False)
    # unique permanent id per domain per platform
    uid = models.CharField(max_length=255, null=False, blank=False)
    handle = models.CharField(max_length=1000, null=False, blank=False)

    access_data = models.JSONField(default=dict, null=False)
    account_data = models.JSONField(default=dict, null=False)
    preference_data = models.JSONField(default=dict, null=False)

    followers = models.JSONField(default=list)
    following = models.JSONField(default=list)
    mutes = models.JSONField(default=list)
    blocks = models.JSONField(default=list)
    domain_blocks = models.JSONField(default=list)

    created = models.DateTimeField(default=timezone.now)
    modified = models.DateTimeField(auto_now=True)
    last_refresh = models.DateTimeField(default=None, null=True)
    last_reachable = models.DateTimeField(default=None, null=True)

    # sync_profile = jsondata.BooleanField(
    #     json_field_name="preference_data", default=True
    # )
    # sync_graph = jsondata.BooleanField(json_field_name="preference_data", default=True)
    # sync_timeline = jsondata.BooleanField(
    #     json_field_name="preference_data", default=True
    # )

    class Meta:
        indexes = [
            models.Index(fields=["type", "handle"], name="index_social_type_handle"),
            models.Index(
                fields=["type", "domain", "uid"], name="index_social_type_domain_uid"
            ),
        ]
        constraints = [
            models.UniqueConstraint(
                Lower("domain"), Lower("uid"), name="unique_social_domain_uid"
            ),
            models.UniqueConstraint(
                "type", Lower("handle"), name="unique_social_type_handle"
            ),
        ]

    def __str__(self) -> str:
        return f"({self.pk}){self.platform}@{self.handle}"

    @property
    def platform(self) -> Platform:
        return Platform(
            str(self.type).replace("mastodon.", "", 1).replace("account", "", 1)
        )

    def to_dict(self):
        # skip cached_property, datetime and other non-serializable fields
        d = {
            k: v
            for k, v in self.__dict__.items()
            if k
            not in [
                "created",
                "modified",
                "last_refresh",
                "last_reachable",
            ]
            and not k.startswith("_")
        }
        return d

    @classmethod
    def from_dict(cls, d: dict | None):
        return cls(**d) if d else None

    def check_alive(self) -> bool:
        return False

    def refresh(self) -> bool:
        return False

    def refresh_graph(self, save=True) -> bool:
        return False

    def sync(self, skip_graph=False, sleep_hours=0) -> bool:
        if self.last_refresh and self.last_refresh > timezone.now() - timedelta(
            hours=sleep_hours
        ):
            logger.debug(f"{self} skip refreshing as it's done recently")
            return False
        if not self.check_alive():
            d = (
                (timezone.now() - self.last_reachable).days
                if self.last_reachable
                else "unknown"
            )
            logger.warning(f"{self} unreachable for {d} days")
            return False
        if not self.refresh():
            logger.warning(f"{self} refresh failed")
            return False
        if not skip_graph:
            self.refresh_graph()
        logger.debug(f"{self} refreshed")
        return True

    def sync_graph(self) -> int:
        return 0
