import inspect
import os
import sys
from importlib import import_module

from django.apps import apps
from django.core.management import call_command
from django.core.management.base import BaseCommand
from django.template import Context, Template

from flexi_tag.utils.models import FlexiTagMixin


class Command(BaseCommand):
    help = "Generate tag models for all models that inherit from FlexiTagMixin"

    def add_arguments(self, parser):
        parser.add_argument(
            "--dry-run",
            action="store_true",
            help="Show what would be generated without creating files",
        )

    def handle(self, *args, **options):
        dry_run = options.get("dry_run", False)
        # Template for the generated model
        model_template = Template(
            """
# This file is auto-generated by the generate_tag_models command via "flexi-tag" package.
# Do not edit manually.

from django.db import models
from flexi_tag.utils.compat import JSONField, GinIndex


class {{ model_name }}Tag(models.Model):
    instance = models.OneToOneField(
        "{{ app_label }}.{{ model_name }}",
        on_delete=models.CASCADE,
        primary_key=True,
    )
    tags = JSONField(default=list)

    class Meta:
        indexes = [GinIndex(fields=["tags"])]

    def __str__(self):
        return "Tags for {}".format(self.instance)
"""
        )

        for app_config in apps.get_app_configs():
            for model in app_config.get_models():
                if issubclass(model, FlexiTagMixin) and model is not FlexiTagMixin:
                    model_name = model.__name__
                    app_label = model._meta.app_label  # noqa
                    model_module = import_module(model.__module__)
                    model_dir = os.path.dirname(inspect.getfile(model_module))

                    context = Context(
                        {
                            "model_name": model_name,
                            "app_label": app_label,
                            "model_lower_name": model_name.lower(),
                        }
                    )

                    model_content = model_template.render(context)

                    file_path = os.path.join(model_dir, "flexi_generated_model.py")

                    if dry_run:
                        self.stdout.write(
                            "Would create file: {file_path}".format(file_path=file_path)
                        )
                        self.stdout.write(model_content)
                        self.stdout.write("-" * 40)
                    else:
                        with open(file_path, "w") as f:
                            f.write(model_content)

                        # Add import to the models.py file
                        models_file_path = os.path.join(model_dir, "models.py")
                        if os.path.exists(models_file_path):
                            with open(models_file_path, "r") as f:
                                models_content = f.read()

                            # Process for handling multiple imports
                            import_pattern = "from .flexi_generated_model import"
                            tag_model_name = "{}Tag".format(model_name)
                            lines = models_content.splitlines()
                            existing_import_line = None
                            last_import_index = -1

                            # Find existing import line and last import line
                            for i, line in enumerate(lines):
                                if line.startswith(import_pattern):
                                    existing_import_line = line
                                    last_import_index = i
                                elif line.startswith("import ") or line.startswith(
                                    "from "
                                ):
                                    if (
                                        existing_import_line is None
                                    ):  # Only update if we haven't found the flexi import
                                        last_import_index = i

                            if existing_import_line:
                                # Check if this tag model is already imported
                                if tag_model_name not in existing_import_line:
                                    # Extract existing imports
                                    existing_imports = existing_import_line.replace(
                                        import_pattern, ""
                                    ).strip()
                                    # Handle case where noqa comment exists
                                    if "#" in existing_imports:
                                        comment_part = existing_imports[
                                            existing_imports.find("#") :
                                        ]
                                        existing_imports = existing_imports[
                                            : existing_imports.find("#")
                                        ].strip()
                                    else:
                                        comment_part = "  # noqa"

                                    # Handle both comma-separated and single imports
                                    import_items = [
                                        item.strip()
                                        for item in existing_imports.split(",")
                                    ]
                                    import_items.append(tag_model_name)

                                    # Create the new import line
                                    new_imports = ", ".join(import_items)
                                    new_import_line = "{} {}{}".format(
                                        import_pattern, new_imports, comment_part
                                    )

                                    # Replace the existing import line
                                    lines[last_import_index] = new_import_line
                                    updated_content = "\n".join(lines)

                                    with open(models_file_path, "w") as f:
                                        f.write(updated_content)
                            else:
                                # No existing import, add a new one
                                # Add import statement after the last import with two newlines
                                if last_import_index >= 0:
                                    # Add the import with # noqa comment
                                    import_line = "\n\nfrom .flexi_generated_model import {}Tag  # noqa".format(
                                        model_name
                                    )
                                    updated_lines = (
                                        lines[: last_import_index + 1]
                                        + [import_line]
                                        + lines[last_import_index + 1 :]
                                    )
                                    updated_content = "\n".join(updated_lines)

                                    with open(models_file_path, "w") as f:
                                        f.write(updated_content)

                        self.stdout.write(
                            self.style.SUCCESS(
                                "Created tag model for {app_label}.{model_name} at {file_path}".format(
                                    app_label=app_label,
                                    model_name=model_name,
                                    file_path=file_path,
                                )
                            )
                        )

        try:
            from django.contrib.postgres.indexes import GinIndex  # noqa
        except ImportError:
            self.stdout.write(
                self.style.WARNING(
                    "Important Note: GinIndex is not available. You need to add it manually with migrations."
                )
            )

        if dry_run:
            self.stdout.write("Dry run completed. No files were created.")
        else:
            self.stdout.write(
                self.style.SUCCESS("Successfully generated all tag models!")
            )
            is_test = (
                "test" in sys.argv
                or os.environ.get("DJANGO_SETTINGS_MODULE")
                == "flexi_tag.tests.test_settings"
            )

            if not is_test:
                self.stdout.write("Running makemigrations command...")
                call_command("makemigrations")
                self.stdout.write(
                    self.style.SUCCESS("Successfully created migrations!")
                )
            else:
                self.stdout.write("Skipping makemigrations in test environment.")
