"""
Vergleicht die Django-Modelle mit dem echten MySQL-Schema.

Hintergrund: Die Tabellen stammen aus dem Laravel-Altsystem und wurden mit
`migrate --fake` / `--fake-initial` uebernommen (siehe Migrations2django.md).
Django hat sie also nie selbst erzeugt -- `null=True` im Modell sagt daher
NICHTS ueber die tatsaechlichen NOT-NULL-Constraints in MySQL aus.

Genau diese Luecke erzeugt IntegrityError 1048 ("Column 'x' cannot be null"):
Django validiert nicht, weil das Modell NULL erlaubt, und MySQL lehnt ab.

Aufruf:
    python manage.py schema_check
    python manage.py schema_check --app app_nutzer
"""
from django.apps import apps
from django.core.management.base import BaseCommand
from django.db import connection


class Command(BaseCommand):
    help = "Vergleicht Modell-Definitionen mit dem echten DB-Schema (NULL-barkeit, fehlende Spalten)."

    def add_arguments(self, parser):
        parser.add_argument('--app', help='Nur diese App pruefen, z.B. app_nutzer')

    def handle(self, *args, **options):
        only_app = options.get('app')
        problems = 0

        with connection.cursor() as cursor:
            cursor.execute("""
                SELECT TABLE_NAME, COLUMN_NAME, IS_NULLABLE, COLUMN_DEFAULT, EXTRA
                FROM INFORMATION_SCHEMA.COLUMNS
                WHERE TABLE_SCHEMA = DATABASE()
            """)
            db = {}
            for table, col, nullable, default, extra in cursor.fetchall():
                db.setdefault(table, {})[col] = {
                    'nullable': nullable == 'YES',
                    'default': default,
                    'extra': extra or '',
                }

        for model in apps.get_models():
            app_label = model._meta.app_label
            if only_app and app_label != only_app:
                continue
            table = model._meta.db_table
            if table not in db:
                continue

            for field in model._meta.concrete_fields:
                col = field.column
                dbcol = db[table].get(col)

                if dbcol is None:
                    self.stdout.write(self.style.ERROR(
                        f"[FEHLT IN DB] {table}.{col}  (Modell {model.__name__}.{field.name})"
                    ))
                    problems += 1
                    continue

                # Der kritische Fall: Modell sagt NULL erlaubt, DB sagt NOT NULL.
                # Ohne DB-Default endet jeder Insert ohne Wert in IntegrityError 1048.
                if field.null and not dbcol['nullable']:
                    has_default = (
                        dbcol['default'] is not None
                        or 'auto_increment' in dbcol['extra']
                        or 'DEFAULT_GENERATED' in dbcol['extra']
                    )
                    marker = "" if has_default else "  <== 1048-Kandidat"
                    style = self.style.WARNING if has_default else self.style.ERROR
                    self.stdout.write(style(
                        f"[NULL-MISMATCH] {table}.{col}: Modell null=True, DB NOT NULL"
                        f" (default={dbcol['default']!r}){marker}"
                    ))
                    problems += 1

                # Umgekehrter Fall: harmlos zur Laufzeit, aber Modell luegt.
                elif not field.null and dbcol['nullable'] and not field.primary_key:
                    self.stdout.write(self.style.NOTICE(
                        f"[INFO] {table}.{col}: Modell null=False, DB erlaubt NULL"
                    ))

        if problems == 0:
            self.stdout.write(self.style.SUCCESS("Keine Abweichungen gefunden."))
        else:
            self.stdout.write(self.style.WARNING(f"\n{problems} Abweichung(en) gefunden."))
