"""One-off management command to backfill placeholder emails."""

import logging
from typing import Any

from django.contrib.auth import get_user_model
from django.core.management.base import BaseCommand, CommandParser
from django.db.models import Q
from django.utils.translation import gettext as _

from aa_skip_email.app_settings import (
    AA_SKIP_EMAIL_DOMAIN,
    AA_SKIP_EMAIL_LIMIT,
)
from aa_skip_email.helpers import get_placeholder_users, make_placeholder_email

logger = logging.getLogger(__name__)

User = get_user_model()


class Command(BaseCommand):
    """
    Manual sibling of the periodic ``fill_missing_emails`` Celery task.

    Defaults to filling only blank / NULL emails. ``--overwrite`` replaces
    existing emails for every user (or only existing placeholders when
    combined with ``--only-placeholders``). ``--dry-run`` previews the
    changes without writing.
    """

    help = _(
        "Fill missing user emails with placeholder addresses (one-off run)."
    )

    def add_arguments(self, parser: CommandParser) -> None:
        """Register --limit / --dry-run / --overwrite / --only-placeholders."""
        parser.add_argument(
            "--limit",
            type=int,
            default=AA_SKIP_EMAIL_LIMIT,
            help=_("Max users to process (default: %(default)d)"),
        )
        parser.add_argument(
            "--dry-run",
            action="store_true",
            help=_("Do not write changes, only show what would be updated."),
        )
        parser.add_argument(
            "--overwrite",
            action="store_true",
            help=_("Overwrite emails for ALL users (dangerous)."),
        )
        parser.add_argument(
            "--only-placeholders",
            action="store_true",
            help=_("Overwrite only emails that already end with @%(domain)s.")
            % {"domain": AA_SKIP_EMAIL_DOMAIN},
        )

    def handle(self, *args: Any, **options: Any) -> None:
        """Walk the matching users and fill / overwrite their email."""
        limit = options["limit"]
        dry_run = options["dry_run"]
        overwrite = options["overwrite"]
        only_placeholders = options["only_placeholders"]
        verbosity = options["verbosity"]

        if overwrite and only_placeholders:
            base_qs = get_placeholder_users()
        elif overwrite:
            base_qs = User.objects.all()
        else:
            base_qs = User.objects.filter(Q(email__isnull=True) | Q(email=""))

        # Short-circuit on the empty-set case via exists() so we skip the
        # full COUNT(*) on auth_user when there is no work to do. The
        # unbounded count() runs only once we know the backfill has rows
        # to process; the resulting "Updated: N/M" line then shows how
        # much work remains for subsequent runs.
        if not base_qs.exists():
            self.stdout.write(
                self.style.SUCCESS(_("No matching users found."))
            )
            return
        total = base_qs.count()

        qs = base_qs.order_by("id").only("id", "email")[:limit]

        mode = "OVERWRITE" if overwrite else "MISSING-ONLY"
        if overwrite and not dry_run:
            self.stdout.write(
                self.style.WARNING(
                    _("WARNING: --overwrite will replace existing emails.")
                )
            )

        updated = 0
        for user in qs.iterator(chunk_size=1000):
            username = user.get_username()
            new_email = make_placeholder_email(username, user.id)

            if dry_run:
                if verbosity >= 2:
                    self.stdout.write(
                        f"[DRY:{mode}] id={user.id} {user.email!r} -> {new_email}"  # noqa: E501
                    )
                updated += 1
                continue

            user.email = new_email
            user.save(update_fields=["email"])
            updated += 1

        if dry_run:
            self.stdout.write(
                self.style.WARNING(
                    _(
                        "Dry-run complete (%(mode)s). Would update: %(updated)d/%(total)d"  # noqa: E501
                    )
                    % {"mode": mode, "updated": updated, "total": total}
                )
            )
        else:
            self.stdout.write(
                self.style.SUCCESS(
                    _("Done (%(mode)s). Updated: %(updated)d/%(total)d")
                    % {"mode": mode, "updated": updated, "total": total}
                )
            )
        logger.info(
            "fill_missing_emails done: mode=%s dry_run=%s updated=%d/%d",
            mode,
            dry_run,
            updated,
            total,
        )
