import urllib.parse
import uuid

from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.db.models import JSONField
from django.db.models.signals import pre_save
from django.dispatch import receiver
from django.urls import reverse
from django.utils import timezone

from funkwhale_api.common import models as common_models
from funkwhale_api.federation import models as federation_models
from funkwhale_api.federation import utils as federation_utils


class InstancePolicyQuerySet(models.QuerySet):
    def active(self):
        return self.filter(is_active=True)

    def matching_url(self, *urls):
        if not urls:
            return self.none()
        query = None
        for url in urls:
            new_query = self.matching_url_query(url)
            if query:
                query = query | new_query
            else:
                query = new_query
        return self.filter(query)

    def matching_url_query(self, url):
        parsed = urllib.parse.urlparse(url)
        return models.Q(target_domain_id=parsed.hostname) | models.Q(
            target_actor__fid=url
        )


class InstancePolicy(models.Model):
    uuid = models.UUIDField(default=uuid.uuid4, unique=True)
    actor = models.ForeignKey(
        "federation.Actor",
        related_name="created_instance_policies",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
    )
    target_domain = models.OneToOneField(
        "federation.Domain",
        related_name="instance_policy",
        on_delete=models.CASCADE,
        null=True,
        blank=True,
    )
    target_actor = models.OneToOneField(
        "federation.Actor",
        related_name="instance_policy",
        on_delete=models.CASCADE,
        null=True,
        blank=True,
    )
    creation_date = models.DateTimeField(default=timezone.now)

    is_active = models.BooleanField(default=True)
    # a summary explaining why the policy is in place
    summary = models.TextField(max_length=10000, null=True, blank=True)
    # either block everything (simpler, but less granularity)
    block_all = models.BooleanField(default=False)
    # or pick individual restrictions below
    # do not show in timelines/notifications, except for actual followers
    silence_activity = models.BooleanField(default=False)
    silence_notifications = models.BooleanField(default=False)
    # do not download any media from the target
    reject_media = models.BooleanField(default=False)

    objects = InstancePolicyQuerySet.as_manager()

    @property
    def target(self):
        if self.target_actor:
            return {"type": "actor", "obj": self.target_actor}
        if self.target_domain_id:
            return {"type": "domain", "obj": self.target_domain}


class UserFilter(models.Model):
    uuid = models.UUIDField(default=uuid.uuid4, unique=True)
    creation_date = models.DateTimeField(default=timezone.now)
    target_artist = models.ForeignKey(
        "music.Artist", on_delete=models.CASCADE, related_name="user_filters"
    )
    user = models.ForeignKey(
        "users.User", on_delete=models.CASCADE, related_name="content_filters"
    )

    class Meta:
        unique_together = ("user", "target_artist")

    @property
    def target(self):
        if self.target_artist:
            return {"type": "artist", "obj": self.target_artist}


REPORT_TYPES = [
    ("takedown_request", "Takedown request"),
    ("invalid_metadata", "Invalid metadata"),
    ("illegal_content", "Illegal content"),
    ("offensive_content", "Offensive content"),
    ("other", "Other"),
]


class Report(federation_models.FederationMixin):
    uuid = models.UUIDField(default=uuid.uuid4, unique=True)
    creation_date = models.DateTimeField(default=timezone.now)
    summary = models.TextField(null=True, blank=True, max_length=50000)
    handled_date = models.DateTimeField(null=True)
    is_handled = models.BooleanField(default=False)
    type = models.CharField(max_length=40, choices=REPORT_TYPES)
    submitter_email = models.EmailField(null=True)
    submitter = models.ForeignKey(
        "federation.Actor",
        related_name="reports",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
    )

    assigned_to = models.ForeignKey(
        "federation.Actor",
        related_name="assigned_reports",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
    )

    target_id = models.IntegerField(null=True)
    target_content_type = models.ForeignKey(
        ContentType, null=True, on_delete=models.CASCADE
    )
    target = GenericForeignKey("target_content_type", "target_id")
    target_owner = models.ForeignKey(
        "federation.Actor", on_delete=models.SET_NULL, null=True, blank=True
    )
    # frozen state of the target being reported, to ensure we still have info in the event of a
    # delete
    target_state = JSONField(null=True)

    notes = GenericRelation(
        "Note", content_type_field="target_content_type", object_id_field="target_id"
    )

    objects = common_models.GenericTargetQuerySet.as_manager()

    def get_federation_id(self):
        if self.fid:
            return self.fid

        return federation_utils.full_url(
            reverse("federation:reports-detail", kwargs={"uuid": self.uuid})
        )

    def save(self, **kwargs):
        if not self.pk and not self.fid:
            self.fid = self.get_federation_id()

        return super().save(**kwargs)


class Note(models.Model):
    uuid = models.UUIDField(default=uuid.uuid4, unique=True)
    creation_date = models.DateTimeField(default=timezone.now)
    summary = models.TextField(max_length=50000)
    author = models.ForeignKey(
        "federation.Actor", related_name="moderation_notes", on_delete=models.CASCADE
    )

    target_id = models.IntegerField(null=True)
    target_content_type = models.ForeignKey(
        ContentType, null=True, on_delete=models.CASCADE
    )
    target = GenericForeignKey("target_content_type", "target_id")


USER_REQUEST_TYPES = [
    ("signup", "Sign-up"),
]

USER_REQUEST_STATUSES = [
    ("pending", "Pending"),
    ("refused", "Refused"),
    ("approved", "Approved"),
]


class UserRequest(models.Model):
    uuid = models.UUIDField(default=uuid.uuid4, unique=True)
    creation_date = models.DateTimeField(default=timezone.now)
    handled_date = models.DateTimeField(null=True)
    type = models.CharField(max_length=40, choices=USER_REQUEST_TYPES)
    status = models.CharField(
        max_length=40, choices=USER_REQUEST_STATUSES, default="pending"
    )
    submitter = models.ForeignKey(
        "federation.Actor",
        related_name="requests",
        on_delete=models.CASCADE,
    )
    assigned_to = models.ForeignKey(
        "federation.Actor",
        related_name="assigned_requests",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
    )

    metadata = JSONField(null=True)

    notes = GenericRelation(
        "Note", content_type_field="target_content_type", object_id_field="target_id"
    )


@receiver(pre_save, sender=Report)
def set_handled_date(sender, instance, **kwargs):
    if instance.is_handled and not instance.handled_date:
        instance.handled_date = timezone.now()
    elif not instance.is_handled:
        instance.handled_date = None
