import fnmatch
import os

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

from compressor.conf import settings
from compressor.cache import cache, get_mtime, get_mtime_cachekey


class Command(BaseCommand):
    help = "Add or remove all mtime values from the cache"

    def add_arguments(self, parser):
        parser.add_argument(
            "-i",
            "--ignore",
            action="append",
            default=[],
            dest="ignore_patterns",
            metavar="PATTERN",
            help="Ignore files or directories matching this glob-style "
            "pattern. Use multiple times to ignore more.",
        ),
        parser.add_argument(
            "--no-default-ignore",
            action="store_false",
            dest="use_default_ignore_patterns",
            default=True,
            help="Don't ignore the common private glob-style patterns 'CVS', "
            "'.*' and '*~'.",
        ),
        parser.add_argument(
            "--follow-links",
            dest="follow_links",
            action="store_true",
            help="Follow symlinks when traversing the COMPRESS_ROOT "
            "(which defaults to STATIC_ROOT). Be aware that using this "
            "can lead to infinite recursion if a link points to a parent "
            "directory of itself.",
        ),
        parser.add_argument(
            "-c", "--clean", dest="clean", action="store_true", help="Remove all items"
        ),
        parser.add_argument(
            "-a", "--add", dest="add", action="store_true", help="Add all items"
        ),

    def is_ignored(self, path):
        """
        Return True or False depending on whether the ``path`` should be
        ignored (if it matches any pattern in ``ignore_patterns``).
        """
        for pattern in self.ignore_patterns:
            if fnmatch.fnmatchcase(path, pattern):
                return True
        return False

    def handle(self, **options):
        ignore_patterns = options["ignore_patterns"]
        if options["use_default_ignore_patterns"]:
            ignore_patterns += ["CVS", ".*", "*~"]
            options["ignore_patterns"] = ignore_patterns
        self.ignore_patterns = ignore_patterns

        if (options["add"] and options["clean"]) or (
            not options["add"] and not options["clean"]
        ):
            raise CommandError('Please specify either "--add" or "--clean"')

        if not settings.COMPRESS_MTIME_DELAY:
            raise CommandError(
                "mtime caching is currently disabled. Please "
                "set the COMPRESS_MTIME_DELAY setting to a number of seconds."
            )

        files_to_add = set()
        keys_to_delete = set()

        for root, dirs, files in os.walk(
            settings.COMPRESS_ROOT, followlinks=options["follow_links"]
        ):
            for dir_ in dirs:
                if self.is_ignored(dir_):
                    dirs.remove(dir_)
            for filename in files:
                common = "".join(root.split(settings.COMPRESS_ROOT))
                if common.startswith(os.sep):
                    common = common[len(os.sep) :]
                if self.is_ignored(os.path.join(common, filename)):
                    continue
                filename = os.path.join(root, filename)
                keys_to_delete.add(get_mtime_cachekey(filename))
                if options["add"]:
                    files_to_add.add(filename)

        if keys_to_delete:
            cache.delete_many(list(keys_to_delete))
            self.stdout.write(
                "Deleted mtimes of %d files from the cache." % len(keys_to_delete)
            )

        if files_to_add:
            for filename in files_to_add:
                get_mtime(filename)
            self.stdout.write("Added mtimes of %d files to cache." % len(files_to_add))
