import os
from argparse import RawTextHelpFormatter

from django.core.management.base import BaseCommand
from django.db import transaction

from funkwhale_api.music import models


def progress(buffer, count, total, status=""):
    bar_len = 60
    filled_len = int(round(bar_len * count / float(total)))

    bar = "=" * filled_len + "-" * (bar_len - filled_len)

    buffer.write(f"[{bar}] {count}/{total} ...{status}\r")
    buffer.flush()


class Command(BaseCommand):
    help = """
    Loop through all in-place imported files in the database, and verify
    that the corresponding files are present on the filesystem. If some files are not
    found and --no-dry-run is specified, the corresponding database objects will be deleted.
    """

    def create_parser(self, *args, **kwargs):
        parser = super().create_parser(*args, **kwargs)
        parser.formatter_class = RawTextHelpFormatter
        return parser

    def add_arguments(self, parser):
        parser.add_argument(
            "--no-dry-run",
            action="store_false",
            dest="dry_run",
            default=True,
            help="Disable dry run mode and apply pruning for real on the database",
        )

    @transaction.atomic
    def handle(self, *args, **options):
        candidates = models.Upload.objects.filter(source__startswith="file://")
        candidates = candidates.filter(audio_file__in=["", None])
        total = candidates.count()
        self.stdout.write(f"Checking {total} in-place imported files…")

        missing = []
        for i, row in enumerate(candidates.values("id", "source").iterator()):
            path = row["source"].replace("file://", "")
            progress(self.stdout, i + 1, total)
            if not os.path.exists(path):
                missing.append((path, row["id"]))

        if missing:
            for path, _ in missing:
                self.stdout.write(f"  {path}")
            self.stdout.write(
                "The previous {} paths are referenced in database, but not found on disk!".format(
                    len(missing)
                )
            )

        else:
            self.stdout.write("All in-place imports have a matching on-disk file")
            return

        to_delete = candidates.filter(pk__in=[id for _, id in missing])
        if options["dry_run"]:
            self.stdout.write(
                "Nothing was deleted, rerun this command with --no-dry-run to apply the changes"
            )
        else:
            self.stdout.write(f"Deleting {to_delete.count()} uploads…")
            to_delete.delete()
