"""
Management command to test FSM transition callback execution.

This command allows testing callbacks for specific transitions
without actually changing model state.
"""

from django.apps import apps
from django.contrib.auth import get_user_model
from django.core.management.base import BaseCommand, CommandError, CommandParser

from apps.core.state_machine.callback_base import (
    CallbackStage,
    TransitionContext,
    callback_registry,
)
from apps.core.state_machine.monitoring import callback_monitor


class Command(BaseCommand):
    help = "Test FSM transition callbacks for specific transitions"

    def add_arguments(self, parser: CommandParser) -> None:
        parser.add_argument(
            "model",
            type=str,
            help="Model name (e.g., EditSubmission, Ride, Park)",
        )
        parser.add_argument(
            "source",
            type=str,
            help="Source state value",
        )
        parser.add_argument(
            "target",
            type=str,
            help="Target state value",
        )
        parser.add_argument(
            "--instance-id",
            type=int,
            help="ID of an existing instance to use for testing",
        )
        parser.add_argument(
            "--user-id",
            type=int,
            help="ID of user to use for testing",
        )
        parser.add_argument(
            "--dry-run",
            action="store_true",
            help="Show what would be executed without running callbacks",
        )
        parser.add_argument(
            "--stage",
            type=str,
            choices=["pre", "post", "error", "all"],
            default="all",
            help="Which callback stage to test",
        )
        parser.add_argument(
            "--field",
            type=str,
            default="status",
            help="FSM field name (default: status)",
        )

    def handle(self, *args, **options):
        model_name = options["model"]
        source = options["source"]
        target = options["target"]
        instance_id = options.get("instance_id")
        user_id = options.get("user_id")
        dry_run = options.get("dry_run", False)
        stage_filter = options.get("stage", "all")
        field_name = options.get("field", "status")

        # Find the model class
        model_class = self._find_model(model_name)
        if not model_class:
            raise CommandError(f"Model '{model_name}' not found")

        # Get or create test instance
        instance = self._get_or_create_instance(model_class, instance_id, source, field_name)

        # Get user if specified
        user = None
        if user_id:
            User = get_user_model()
            try:
                user = User.objects.get(pk=user_id)
            except User.DoesNotExist:
                raise CommandError(f"User with ID {user_id} not found") from None

        # Create transition context
        context = TransitionContext(
            instance=instance,
            field_name=field_name,
            source_state=source,
            target_state=target,
            user=user,
        )

        self.stdout.write(
            self.style.SUCCESS(
                f"\n=== Testing Transition Callbacks ===\n"
                f"Model: {model_name}\n"
                f"Transition: {source} → {target}\n"
                f"Field: {field_name}\n"
                f"Instance: {instance}\n"
                f"User: {user}\n"
                f"Dry Run: {dry_run}\n"
            )
        )

        # Get callbacks for each stage
        stages_to_test = []
        if stage_filter == "all":
            stages_to_test = [CallbackStage.PRE, CallbackStage.POST, CallbackStage.ERROR]
        else:
            stages_to_test = [CallbackStage(stage_filter)]

        total_callbacks = 0
        total_success = 0
        total_failures = 0

        for stage in stages_to_test:
            callbacks = callback_registry.get_callbacks(model_class, field_name, source, target, stage)

            if not callbacks:
                self.stdout.write(self.style.WARNING(f"\nNo {stage.value.upper()} callbacks registered"))
                continue

            self.stdout.write(self.style.WARNING(f"\n{stage.value.upper()} Callbacks ({len(callbacks)}):"))
            self.stdout.write("-" * 50)

            for callback in callbacks:
                total_callbacks += 1
                callback_info = (
                    f"  {callback.name} (priority: {callback.priority}, "
                    f"continue_on_error: {callback.continue_on_error})"
                )

                if dry_run:
                    self.stdout.write(callback_info)
                    self.stdout.write(self.style.NOTICE("    → Would execute (dry run)"))
                else:
                    self.stdout.write(callback_info)

                    # Check should_execute
                    if not callback.should_execute(context):
                        self.stdout.write(self.style.WARNING("    → Skipped (should_execute returned False)"))
                        continue

                    # Execute callback
                    try:
                        if stage == CallbackStage.ERROR:
                            result = callback.execute(context, exception=Exception("Test exception"))
                        else:
                            result = callback.execute(context)

                        if result:
                            self.stdout.write(self.style.SUCCESS("    → Success"))
                            total_success += 1
                        else:
                            self.stdout.write(self.style.ERROR("    → Failed (returned False)"))
                            total_failures += 1

                    except Exception as e:
                        self.stdout.write(self.style.ERROR(f"    → Exception: {type(e).__name__}: {e}"))
                        total_failures += 1

        # Summary
        self.stdout.write("\n" + "=" * 50)
        self.stdout.write(self.style.SUCCESS(f"Total callbacks: {total_callbacks}"))
        if not dry_run:
            self.stdout.write(self.style.SUCCESS(f"Successful: {total_success}"))
            self.stdout.write(
                self.style.ERROR(f"Failed: {total_failures}")
                if total_failures
                else self.style.SUCCESS(f"Failed: {total_failures}")
            )

        # Show monitoring stats if available
        if not dry_run:
            self.stdout.write(self.style.WARNING("\nRecent Executions:"))
            recent = callback_monitor.get_recent_executions(limit=10)
            for record in recent:
                status = "✓" if record.success else "✗"
                self.stdout.write(f"  {status} {record.callback_name} [{record.duration_ms:.2f}ms]")

    def _find_model(self, model_name):
        """Find a model class by name."""
        for app_config in apps.get_app_configs():
            try:
                model = app_config.get_model(model_name)
                return model
            except LookupError:
                continue
        return None

    def _get_or_create_instance(self, model_class, instance_id, source, field_name):
        """Get an existing instance or create a mock one."""
        if instance_id:
            try:
                return model_class.objects.get(pk=instance_id)
            except model_class.DoesNotExist:
                raise CommandError(f"{model_class.__name__} with ID {instance_id} not found") from None

        # Create a mock instance for testing
        # This won't be saved to the database
        instance = model_class()
        instance.pk = 0  # Fake ID
        setattr(instance, field_name, source)

        self.stdout.write(self.style.NOTICE("Using mock instance (no --instance-id provided)"))

        return instance
