import json
import logging
import sys
import uuid

from django.core import validators
from django.core.management.base import BaseCommand, CommandError

from funkwhale_api.common import session
from funkwhale_api.federation import models
from funkwhale_api.moderation import mrf


def is_uuid(v):
    try:
        uuid.UUID(v)
    except ValueError:
        return False
    return True


def is_url(v):
    validator = validators.URLValidator()
    try:
        validator(v)
    except (ValueError, validators.ValidationError):
        return False

    return True


class Command(BaseCommand):
    help = "Check a given message against all or a specific MRF rule"

    def add_arguments(self, parser):
        parser.add_argument(
            "type",
            type=str,
            choices=["inbox"],
            help=("The type of MRF. Only inbox is supported at the moment"),
        )
        parser.add_argument(
            "input",
            nargs="?",
            help=(
                "The path to a file containing JSON data. Use - to read from stdin. "
                "If no input is provided, registered MRF policies will be listed "
                "instead.",
            ),
        )
        parser.add_argument(
            "--policy",
            "-p",
            dest="policies",
            nargs="+",
            default=False,
            help="Restrict to a list of MRF policies that will be applied, in that order",
        )

    def handle(self, *args, **options):
        logger = logging.getLogger("funkwhale.mrf")
        logger.setLevel(logging.DEBUG)
        logger.addHandler(logging.StreamHandler(stream=sys.stderr))

        input = options["input"]
        if not input:
            registry = getattr(mrf, options["type"])
            self.stdout.write(
                "No input given, listing registered policies for '{}' MRF:".format(
                    options["type"]
                )
            )
            for name in registry.keys():
                self.stdout.write(f"- {name}")
            return
        raw_content = None
        content = None
        if input == "-":
            raw_content = sys.stdin.read()
        elif is_uuid(input):
            self.stderr.write("UUID provided, retrieving payload from db")
            content = models.Activity.objects.get(uuid=input).payload
        elif is_url(input):
            response = session.get_session().get(
                input,
                headers={"Accept": "application/activity+json"},
            )
            response.raise_for_status()
            content = response.json()
        else:
            with open(input, "rb") as f:
                raw_content = f.read()
        content = json.loads(raw_content) if content is None else content

        policies = options["policies"] or []
        registry = getattr(mrf, options["type"])
        for policy in policies:
            if policy not in registry:
                raise CommandError(
                    "Unknown policy '{}' for MRF '{}'".format(policy, options["type"])
                )

        payload, updated = registry.apply(content, policies=policies)
        if not payload:
            self.stderr.write("Payload was discarded by MRF")
        elif updated:
            self.stderr.write("Payload was modified by MRF")
            self.stderr.write("Initial payload:\n")
            self.stdout.write(json.dumps(content, indent=2, sort_keys=True))
            self.stderr.write("Modified payload:\n")
            self.stdout.write(json.dumps(payload, indent=2, sort_keys=True))
        else:
            self.stderr.write("Payload left untouched by MRF")
