import re

from django.core.validators import MaxValueValidator, MinValueValidator, RegexValidator
from django.db import models
from django.forms.models import ModelForm, ModelFormMetaclass
from django.utils.translation import gettext_lazy as _


def generate_django_model(schema, group, version, kind):
    """
    Generates a virtual Django model from a Kubernetes CRD's OpenAPI v3 schema.
    """
    spec = schema["properties"].get("spec") or {}
    # defaults = {"apiVersion": f"{group}/{version}", "kind": kind}

    model_fields = {"__module__": "crd_models"}
    model_fields.update(build_object_fields(spec, "spec"))

    meta_class = type("Meta", (), {"app_label": "crd_models"})
    model_fields["Meta"] = meta_class

    # create the model class
    model_name = kind
    model_class = type(model_name, (models.Model,), model_fields)
    return model_class


def build_object_fields(schema, name, verbose_name_prefix=None):
    required_fields = schema.get("required", [])
    properties = schema.get("properties", {})
    fields = {}

    for field_name, field_schema in properties.items():
        is_required = field_name in required_fields
        full_name = f"{name}.{field_name}"
        result = get_django_field(
            field_schema,
            is_required,
            field_name,
            full_name,
            verbose_name_prefix=verbose_name_prefix,
        )
        if isinstance(result, dict):
            fields.update(result)
        else:
            fields[full_name] = result
    return fields


def deslugify(title):
    if "_" in title:
        title.replace("_", " ")
        return title.title()
    return re.sub(r"(?<!^)(?=[A-Z])", " ", title).capitalize()


def get_django_field(
    field_schema, is_required, field_name, full_name, verbose_name_prefix=None
):
    field_type = field_schema.get("type") or "string"
    format = field_schema.get("format")
    verbose_name_prefix = verbose_name_prefix or ""
    verbose_name = f"{verbose_name_prefix} {deslugify(field_name)}".strip()

    kwargs = {
        "blank": not is_required,
        "null": not is_required,
        "help_text": field_schema.get("description"),
        "validators": [],
        "verbose_name": verbose_name,
        "default": field_schema.get("default"),
    }

    if minimum := field_schema.get("minimum"):
        kwargs["validators"].append(MinValueValidator(minimum))
    if maximum := field_schema.get("maximum"):
        kwargs["validators"].append(MaxValueValidator(maximum))

    if field_type == "string":
        if format == "date-time":
            return models.DateTimeField(**kwargs)
        elif format == "date":
            return models.DateField(**kwargs)
        else:
            max_length = field_schema.get("max_length") or 255
            if pattern := field_schema.get("pattern"):
                kwargs["validators"].append(RegexValidator(regex=pattern))
            if choices := field_schema.get("enum"):
                kwargs["choices"] = ((choice, choice) for choice in choices)
            return models.CharField(max_length=max_length, **kwargs)
    elif field_type == "integer":
        return models.IntegerField(**kwargs)
    elif field_type == "number":
        return models.FloatField(**kwargs)
    elif field_type == "boolean":
        return models.BooleanField(**kwargs)
    elif field_type == "object":
        return build_object_fields(
            field_schema, full_name, verbose_name_prefix=f"{verbose_name}:"
        )
    elif field_type == "array":
        # TODO: handle items / validate items, build multi-select input
        # if field_schema.get("items") and (choices := field_schema["items"].get("enum")):
        #   choices = [c, c for c in choices]
        kwargs["help_text"] = _("JSON field (array)")
        return models.JSONField(**kwargs)
    return models.CharField(max_length=255, **kwargs)


class CrdModelFormMixin:
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # self.fields["apiVersion"].disabled = True
        # self.fields["kind"].disabled = True


def generate_model_form_class(model):
    meta_attrs = {
        "model": model,
        "fields": "__all__",
    }
    fields = {
        "Meta": type("Meta", (object,), meta_attrs),
        "__module__": "crd_models",
    }
    class_name = f"{model.__name__}ModelForm"
    return ModelFormMetaclass(class_name, (CrdModelFormMixin, ModelForm), fields)
