# Standard Library
import re
from pathlib import Path

# Django
from django.core.management.base import BaseCommand

# Third party
import polib

# Project
# Local
from django_deepl_po.utils import (
    PO_FILE_EXTENSION,
    PO_FILE_NAME,
    get_all_languages,
    get_apps_name,
    get_locale_path,
)

# Placeholders with format specifier in source: %(name)s, %(name)d, %(name)f
_SOURCE_PLACEHOLDER = re.compile(r"%\(([^)]+)\)([sdf])")
# Broken placeholder in translation: %(name) not followed by s, d, or f
_BROKEN_PLACEHOLDER = re.compile(r"%\([^)]+\)(?!s|d|f)")


def _has_broken_placeholder(text):
    """Return True if text has %(name) without format specifier s/d/f."""
    return bool(_BROKEN_PLACEHOLDER.search(text)) if text else False


def _source_has_placeholders(text):
    """Return True if source has python-format placeholders."""
    return bool(_SOURCE_PLACEHOLDER.search(text)) if text else False


class Command(BaseCommand):
    help = (
        "Clear msgstr entries that have invalid Python format (e.g. %(nombre) instead of "
        "%(nombre)s). Use before re-translating with --overwrite."
    )

    def add_arguments(self, parser):
        parser.add_argument(
            "--language",
            nargs="*",
            default=get_all_languages(),
            help="Language(s) to process. Default: all.",
        )
        parser.add_argument(
            "--dry-run",
            action="store_true",
            help="Show what would be cleared without modifying files.",
        )

    def handle(self, *args, **options):
        languages = options["language"]
        dry_run = options["dry_run"]
        apps = get_apps_name()

        cleared_total = 0
        for app in apps:
            locale_path = Path(get_locale_path(app))
            if not locale_path.exists():
                continue
            for lang in languages:
                po_path = (
                    locale_path
                    / lang
                    / "LC_MESSAGES"
                    / f"{PO_FILE_NAME}{PO_FILE_EXTENSION}"
                )
                if not po_path.exists():
                    continue
                cleared = self._clear_file(po_path, dry_run)
                if cleared:
                    cleared_total += cleared
                    self.stdout.write(
                        self.style.SUCCESS(
                            f"Cleared {cleared} invalid translation(s) in {po_path}"
                        )
                    )

        if dry_run and cleared_total:
            self.stdout.write(
                self.style.WARNING("\nDry run. Use without --dry-run to apply.")
            )
        self.stdout.write(
            self.style.SUCCESS(
                f"\nDone. Cleared {cleared_total} invalid translation(s)."
            )
        )

    def _clear_file(self, po_path, dry_run):
        po = polib.pofile(str(po_path))
        cleared = 0
        for entry in po:
            if not entry.msgid:
                continue
            # Check singular
            if _source_has_placeholders(entry.msgid) and _has_broken_placeholder(
                entry.msgstr
            ):
                if not dry_run:
                    entry.msgstr = ""
                cleared += 1
            # Check plural
            msgid_plural = getattr(entry, "msgid_plural", None)
            msgstr_plural = getattr(entry, "msgstr_plural", None)
            if msgstr_plural and (
                _source_has_placeholders(entry.msgid)
                or (msgid_plural and _source_has_placeholders(msgid_plural))
            ):
                for i, text in msgstr_plural.items():
                    if _has_broken_placeholder(text):
                        if not dry_run:
                            entry.msgstr_plural[i] = ""
                        cleared += 1
        if cleared and not dry_run:
            po.save(str(po_path))
        return cleared
