
# TODO: move to lara_django_base


from django.db import models

def create_through_model(from_model, to_model, field_name, extra_field_model=None):
    """
    Dynamically creates a through model for a ManyToMany relationship.
    """
    if extra_field_model is None:
        # A default can be provided here if needed, or raise an error.
        raise ValueError("extra_field_model must be provided.")

    # Generate a unique name for the through model
    model_name = f'{from_model.__name__}{field_name.capitalize()}{to_model.__name__}Through'

    # Define the fields for the new model
    attrs = {
        '__module__': from_model.__module__,
        from_model.__name__.lower(): models.ForeignKey(from_model, on_delete=models.CASCADE),
        to_model.__name__.lower(): models.ForeignKey(to_model, on_delete=models.CASCADE),
        'extra_field': models.ForeignKey(extra_field_model, on_delete=models.SET_NULL, null=True, blank=True),
        'Meta': type('Meta', (), {
            'unique_together': ((from_model.__name__.lower(), to_model.__name__.lower(), 'extra_field'),),
            'db_table': f'{from_model._meta.app_label}_{model_name.lower()}'
        })
    }

    # Create the model class
    through_model = type(model_name, (models.Model,), attrs)
    return through_model

def with_through_model_fields(extra_field_model_map):
    """
    A class decorator to add ManyToMany fields with a through model to subclasses.
    `extra_field_model_map` is a dictionary where keys are field names
    and values are the extra field models.
    """
    def decorator(cls):
        if not hasattr(cls, '_through_fields'):
            cls._through_fields = {}

        for field_name, extra_model in extra_field_model_map.items():
            # We store the extra model information in the class.
            # The actual ManyToMany field will be created on the subclasses.
            cls._through_fields[field_name] = extra_model

        # Keep track of original __init_subclass__
        original_init_subclass = cls.__init_subclass__

        @classmethod
        def __init_subclass__(cls, **kwargs):
            # Call original __init_subclass__
            original_init_subclass(**kwargs)

            if not cls._meta.abstract:
                for field_name, extra_model in getattr(cls, '_through_fields', {}).items():
                    # This is a concrete class, so we add the M2M field.
                    # The related model for the M2M is determined by looking at the original,
                    # placeholder field on the abstract parent.
                    
                    # Find the placeholder field in the abstract base classes
                    placeholder_field = None
                    for base in cls.__mro__:
                        if hasattr(base, field_name) and isinstance(getattr(base, field_name), models.ManyToManyField):
                            placeholder_field = getattr(base, field_name)
                            break
                    
                    if placeholder_field:
                        to_model = placeholder_field.related_model
                        
                        through_model = create_through_model(
                            from_model=cls,
                            to_model=to_model,
                            field_name=field_name,
                            extra_field_model=extra_model
                        )
                        
                        # Add the through model to the class's module to make it discoverable by Django.
                        import sys
                        setattr(sys.modules[cls.__module__], through_model.__name__, through_model)

                        m2m_field = models.ManyToManyField(
                            to=to_model,
                            through=through_model,
                            related_name=f'{cls.__name__.lower()}_{field_name}',
                            blank=True
                        )
                        cls.add_to_class(field_name, m2m_field)

        cls.__init_subclass__ = __init_subclass__
        return cls
    return decorator
