import os
import shutil

from frappe.utils import get_bench_path


def setup_locale():
    bench_path = get_bench_path()
    locale_dir = os.path.join(bench_path, "apps", "az_locale", "az_locale", "locale")

    copied = 0
    merged = 0
    skipped = 0

    for entry in sorted(os.listdir(locale_dir)):
        if not entry.startswith("translations_") or not os.path.isdir(os.path.join(locale_dir, entry)):
            continue

        lang = entry.replace("translations_", "", 1)
        lang_dir = os.path.join(locale_dir, entry)

        for app_name in sorted(os.listdir(lang_dir)):
            app_dir = os.path.join(lang_dir, app_name)
            if not os.path.isdir(app_dir):
                continue

            source_file = os.path.join(app_dir, f"{lang}.po")
            if not os.path.exists(source_file):
                print(f"[az_locale] Warning: {source_file} not found, skipping")
                skipped += 1
                continue

            dest_dir = os.path.join(bench_path, "apps", app_name, app_name, "locale")
            if not os.path.isdir(dest_dir):
                print(f"[az_locale] Skipping {app_name}/{lang}.po: app not installed")
                skipped += 1
                continue

            dest_file = os.path.join(dest_dir, f"{lang}.po")
            try:
                shutil.copy2(source_file, dest_file)
                print(f"[az_locale] Copied {lang}.po -> {app_name}")
                copied += 1
            except Exception as e:
                print(f"[az_locale] Error copying to {dest_file}: {e}")
                skipped += 1
                continue

            extras_file = os.path.join(app_dir, "extras.po")
            if os.path.exists(extras_file):
                added = _merge_extras(dest_file, extras_file)
                if added is not None:
                    print(f"[az_locale] Merged {added} extras into {app_name}/{lang}.po")
                    merged += added

            # Frappe's runtime reads compiled .mo files (sites/assets/locale/),
            # not the .po we just wrote. Recompile with force=True so our changes
            # take effect — the standard build/migrate compile skips by mtime and
            # would otherwise keep serving a stale .mo.
            _compile_mo(app_name, lang)

    print(
        f"[az_locale] Locale setup complete: {copied} file(s) copied, "
        f"{merged} extras merged, {skipped} skipped"
    )


def _compile_mo(app_name, lang):
    """Compile the app's <lang>.po into the runtime .mo under sites/assets/locale/.

    Frappe serves translations from compiled .mo files, so a freshly written .po
    is invisible until recompiled. We force it because _compile_translation skips
    when an older .mo looks newer than the .po by mtime.
    """
    try:
        from frappe.gettext.translate import _compile_translation
    except ImportError as e:
        print(f"[az_locale] Error: cannot import compiler ({e}), skipping .mo compile")
        return

    try:
        _compile_translation(app_name, lang, force=True)
        print(f"[az_locale] Compiled {app_name}/{lang}.mo")
    except Exception as e:
        print(f"[az_locale] Error compiling {app_name}/{lang}.mo: {e}")


def _merge_extras(dest_file, extras_file):
    """Overlay msgid/msgstr pairs from `extras_file` onto `dest_file`.

    Extras wins on conflict and revives any matching obsolete entries
    (those marked `#~` by msgmerge after upstream .pot regeneration).
    Returns the count of merged messages, or None on error.
    """
    try:
        from babel.messages.pofile import read_po, write_po
    except ImportError as e:
        print(f"[az_locale] Error: babel.messages not available ({e}), skipping extras merge")
        return None

    try:
        with open(dest_file, "rb") as f:
            dest = read_po(f)
        with open(extras_file, "rb") as f:
            extras = read_po(f)
    except Exception as e:
        print(f"[az_locale] Error reading PO files for merge ({dest_file}): {e}")
        return None

    count = 0
    for msg in extras:
        if not msg.id:
            continue
        # Resurrect from obsolete section if upstream pot dropped this msgid.
        try:
            dest.obsolete.pop(msg.id, None)
        except AttributeError:
            pass
        dest[msg.id] = msg
        # babel's Catalog.__setitem__ only merges metadata for an existing key —
        # it does NOT overwrite the translation string. Force it so extras wins
        # on conflict (otherwise we can add new msgids but never correct an
        # existing upstream translation).
        existing = dest.get(msg.id, msg.context)
        if existing is not None:
            existing.string = msg.string
        count += 1

    try:
        with open(dest_file, "wb") as f:
            write_po(f, dest, sort_output=True)
    except Exception as e:
        print(f"[az_locale] Error writing merged PO file {dest_file}: {e}")
        return None

    return count
