"""
Django management command to clear all types of cache data.

This command provides comprehensive cache clearing functionality including:
- Django cache framework (all configured backends)
- Python __pycache__ directories and .pyc files
- Static files cache
- Session cache
- Template cache
- Tailwind CSS build cache
- OPcache (if available)
"""

import shutil
import subprocess
from pathlib import Path

from django.conf import settings
from django.core.cache import cache, caches
from django.core.management.base import BaseCommand


class Command(BaseCommand):
    help = "Clear all types of cache data including Django cache, " "__pycache__, and build caches"

    def add_arguments(self, parser):
        parser.add_argument(
            "--django-cache",
            action="store_true",
            help="Clear Django cache framework cache only",
        )
        parser.add_argument(
            "--pycache",
            action="store_true",
            help="Clear Python __pycache__ directories and .pyc files only",
        )
        parser.add_argument(
            "--static",
            action="store_true",
            help="Clear static files cache only",
        )
        parser.add_argument(
            "--sessions",
            action="store_true",
            help="Clear session cache only",
        )
        parser.add_argument(
            "--templates",
            action="store_true",
            help="Clear template cache only",
        )
        parser.add_argument(
            "--tailwind",
            action="store_true",
            help="Clear Tailwind CSS build cache only",
        )
        parser.add_argument(
            "--opcache",
            action="store_true",
            help="Clear PHP OPcache if available",
        )
        parser.add_argument(
            "--dry-run",
            action="store_true",
            help="Show what would be cleared without actually clearing",
        )
        parser.add_argument(
            "--verbose",
            action="store_true",
            help="Show detailed output of clearing operations",
        )

    def handle(self, *args, **options):
        """Clear cache data based on provided options."""
        self.dry_run = options["dry_run"]
        self.verbose = options["verbose"]

        # If no specific cache type is specified, clear all
        clear_all = not any(
            [
                options["django_cache"],
                options["pycache"],
                options["static"],
                options["sessions"],
                options["templates"],
                options["tailwind"],
                options["opcache"],
            ]
        )

        if self.dry_run:
            self.stdout.write(self.style.WARNING("🔍 DRY RUN MODE - No files will be deleted"))
            self.stdout.write("")

        self.stdout.write(self.style.SUCCESS("🧹 ThrillWiki Cache Clearing Utility"))
        self.stdout.write("")

        # Clear Django cache framework
        if clear_all or options["django_cache"]:
            self.clear_django_cache()

        # Clear Python __pycache__
        if clear_all or options["pycache"]:
            self.clear_pycache()

        # Clear static files cache
        if clear_all or options["static"]:
            self.clear_static_cache()

        # Clear sessions cache
        if clear_all or options["sessions"]:
            self.clear_sessions_cache()

        # Clear template cache
        if clear_all or options["templates"]:
            self.clear_template_cache()

        # Clear Tailwind cache
        if clear_all or options["tailwind"]:
            self.clear_tailwind_cache()

        # Clear OPcache
        if clear_all or options["opcache"]:
            self.clear_opcache()

        self.stdout.write("")
        self.stdout.write(self.style.SUCCESS("✅ Cache clearing completed successfully!"))

    def clear_django_cache(self):
        """Clear Django cache framework cache."""
        self.stdout.write("🗄️  Clearing Django cache framework...")

        try:
            # Clear default cache
            if not self.dry_run:
                cache.clear()

            cache_info = f"Default cache ({cache.__class__.__name__})"
            self.stdout.write(self.style.SUCCESS(f"  ✅ Cleared {cache_info}"))

            # Clear all configured caches
            cache_aliases = getattr(settings, "CACHES", {}).keys()
            for alias in cache_aliases:
                if alias != "default":  # Already cleared above
                    try:
                        cache_backend = caches[alias]
                        if not self.dry_run:
                            cache_backend.clear()

                        cache_info = f"{alias} cache ({cache_backend.__class__.__name__})"
                        self.stdout.write(self.style.SUCCESS(f"  ✅ Cleared {cache_info}"))
                    except Exception as e:
                        self.stdout.write(self.style.WARNING(f"  ⚠️  Could not clear {alias} cache: {e}"))

        except Exception as e:
            self.stdout.write(self.style.ERROR(f"  ❌ Error clearing Django cache: {e}"))

    def clear_pycache(self):
        """Clear Python __pycache__ directories and .pyc files."""
        self.stdout.write("🐍 Clearing Python __pycache__ and .pyc files...")

        removed_count = 0
        removed_size = 0

        try:
            # Start from project root
            project_root = Path(settings.BASE_DIR)

            # Find and remove __pycache__ directories
            for pycache_dir in project_root.rglob("__pycache__"):
                if pycache_dir.is_dir():
                    try:
                        # Calculate size before removal
                        dir_size = sum(f.stat().st_size for f in pycache_dir.rglob("*") if f.is_file())
                        removed_size += dir_size

                        if self.verbose:
                            self.stdout.write(f"  🗑️  Removing: {pycache_dir}")

                        if not self.dry_run:
                            shutil.rmtree(pycache_dir)

                        removed_count += 1
                    except Exception as e:
                        self.stdout.write(self.style.WARNING(f"  ⚠️  Could not remove {pycache_dir}: {e}"))

            # Find and remove .pyc files
            for pyc_file in project_root.rglob("*.pyc"):
                try:
                    file_size = pyc_file.stat().st_size
                    removed_size += file_size

                    if self.verbose:
                        self.stdout.write(f"  🗑️  Removing: {pyc_file}")

                    if not self.dry_run:
                        pyc_file.unlink()

                    removed_count += 1
                except Exception as e:
                    self.stdout.write(self.style.WARNING(f"  ⚠️  Could not remove {pyc_file}: {e}"))

            # Format file size
            size_mb = removed_size / (1024 * 1024)
            self.stdout.write(self.style.SUCCESS(f"  ✅ Removed {removed_count} Python cache items ({size_mb:.2f} MB)"))

        except Exception as e:
            self.stdout.write(self.style.ERROR(f"  ❌ Error clearing Python cache: {e}"))

    def clear_static_cache(self):
        """Clear static files cache."""
        self.stdout.write("📦 Clearing static files cache...")

        try:
            static_root = getattr(settings, "STATIC_ROOT", None)

            if static_root and Path(static_root).exists():
                static_path = Path(static_root)

                # Calculate size
                total_size = sum(f.stat().st_size for f in static_path.rglob("*") if f.is_file())
                size_mb = total_size / (1024 * 1024)

                if self.verbose:
                    self.stdout.write(f"  🗑️  Removing: {static_path}")

                if not self.dry_run:
                    shutil.rmtree(static_path)
                    static_path.mkdir(parents=True, exist_ok=True)

                self.stdout.write(self.style.SUCCESS(f"  ✅ Cleared static files cache ({size_mb:.2f} MB)"))
            else:
                self.stdout.write(self.style.WARNING("  ⚠️  No STATIC_ROOT configured or directory doesn't exist"))

        except Exception as e:
            self.stdout.write(self.style.ERROR(f"  ❌ Error clearing static cache: {e}"))

    def clear_sessions_cache(self):
        """Clear session cache if using cache-based sessions."""
        self.stdout.write("🔐 Clearing session cache...")

        try:
            session_engine = getattr(settings, "SESSION_ENGINE", "")

            if "cache" in session_engine:
                # Using cache-based sessions
                session_cache_alias = getattr(settings, "SESSION_CACHE_ALIAS", "default")
                session_cache = caches[session_cache_alias]

                if not self.dry_run:
                    # Clear session keys (this is a simplified approach)
                    # In production, you might want more sophisticated session clearing
                    session_cache.clear()

                self.stdout.write(self.style.SUCCESS(f"  ✅ Cleared cache-based sessions ({session_cache_alias})"))
            else:
                self.stdout.write(self.style.WARNING("  ⚠️  Not using cache-based sessions"))

        except Exception as e:
            self.stdout.write(self.style.ERROR(f"  ❌ Error clearing session cache: {e}"))

    def clear_template_cache(self):
        """Clear template cache."""
        self.stdout.write("📄 Clearing template cache...")

        try:
            # Clear template cache if using cached template loader
            from django.template import engines
            from django.template.loaders.cached import Loader as CachedLoader

            cleared_engines = 0
            for engine in engines.all():
                try:
                    # Check for DjangoTemplates engine with cached loaders
                    engine_backend = getattr(engine, "backend", "")
                    if "DjangoTemplates" in engine_backend:
                        # Get engine instance safely
                        engine_instance = getattr(engine, "engine", None)
                        if engine_instance:
                            template_loaders = getattr(engine_instance, "template_loaders", [])
                            for loader in template_loaders:
                                if isinstance(loader, CachedLoader):
                                    if not self.dry_run:
                                        loader.reset()
                                    cleared_engines += 1
                                    if self.verbose:
                                        self.stdout.write(f"  🗑️  Cleared cached loader: {loader}")

                    # Check for Jinja2 engines (if present)
                    elif "Jinja2" in engine_backend and hasattr(engine, "env"):
                        env = getattr(engine, "env", None)
                        if env and hasattr(env, "cache"):
                            if not self.dry_run:
                                env.cache.clear()
                            cleared_engines += 1
                            if self.verbose:
                                self.stdout.write(f"  🗑️  Cleared Jinja2 cache: {engine}")

                except Exception as e:
                    if self.verbose:
                        self.stdout.write(self.style.WARNING(f"  ⚠️  Could not clear cache for engine {engine}: {e}"))

            if cleared_engines > 0:
                self.stdout.write(
                    self.style.SUCCESS(f"  ✅ Cleared template cache for " f"{cleared_engines} loaders/engines")
                )
            else:
                self.stdout.write(self.style.WARNING("  ⚠️  No cached template loaders found"))

        except Exception as e:
            self.stdout.write(self.style.ERROR(f"  ❌ Error clearing template cache: {e}"))

    def clear_tailwind_cache(self):
        """Clear Tailwind CSS build cache."""
        self.stdout.write("🎨 Clearing Tailwind CSS cache...")

        try:
            # Look for common Tailwind cache directories
            project_root = Path(settings.BASE_DIR)
            cache_paths = [
                project_root / "node_modules" / ".cache",
                project_root / ".tailwindcss-cache",
                project_root / "static" / "css" / ".cache",
            ]

            cleared_count = 0
            for cache_path in cache_paths:
                if cache_path.exists():
                    try:
                        if self.verbose:
                            self.stdout.write(f"  🗑️  Removing: {cache_path}")

                        if not self.dry_run:
                            if cache_path.is_file():
                                cache_path.unlink()
                            else:
                                shutil.rmtree(cache_path)

                        cleared_count += 1
                    except Exception as e:
                        self.stdout.write(self.style.WARNING(f"  ⚠️  Could not remove {cache_path}: {e}"))

            if cleared_count > 0:
                self.stdout.write(self.style.SUCCESS(f"  ✅ Cleared {cleared_count} Tailwind cache directories"))
            else:
                self.stdout.write(self.style.WARNING("  ⚠️  No Tailwind cache directories found"))

        except Exception as e:
            self.stdout.write(self.style.ERROR(f"  ❌ Error clearing Tailwind cache: {e}"))

    def clear_opcache(self):
        """Clear PHP OPcache if available."""
        self.stdout.write("⚡ Clearing OPcache...")

        try:
            # This is mainly for mixed environments
            php_code = (
                "if (function_exists('opcache_reset')) { "
                "opcache_reset(); echo 'cleared'; } "
                "else { echo 'not_available'; }"
            )
            result = subprocess.run(
                ["php", "-r", php_code],
                capture_output=True,
                text=True,
                timeout=10,
            )

            if result.returncode == 0:
                if "cleared" in result.stdout:
                    self.stdout.write(self.style.SUCCESS("  ✅ OPcache cleared successfully"))
                else:
                    self.stdout.write(self.style.WARNING("  ⚠️  OPcache not available"))
            else:
                self.stdout.write(self.style.WARNING("  ⚠️  PHP not available or OPcache not accessible"))

        except (subprocess.TimeoutExpired, FileNotFoundError):
            self.stdout.write(self.style.WARNING("  ⚠️  PHP not found or not accessible"))
        except Exception as e:
            self.stdout.write(self.style.ERROR(f"  ❌ Error clearing OPcache: {e}"))
