# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this,
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
"""
Test comm-l10n Fluent migrations
"""

import logging
import os
import re
import shutil

import hglib
from compare_locales.merge import merge_channels
from compare_locales.paths.configparser import TOMLParser
from compare_locales.paths.files import ProjectFiles
from fluent.migratetb import validator
from test_fluent_migrations.fmt import diff_resources

import mozpack.path as mozpath
from mach.util import get_state_dir
from mozversioncontrol.repoupdate import update_mercurial_repo

from .l10n_merge import COMM_L10N


def inspect_migration(path):
    """Validate recipe and extract some metadata."""
    return validator.Validator.validate(path)


def prepare_object_dir(cmd):
    """Prepare object dir to have an up-to-date clone of comm-l10n.

    We run this once per mach invocation, for all tested migrations.
    """
    obj_dir = mozpath.join(cmd.topobjdir, "comm", "python", "l10n")
    if not os.path.exists(obj_dir):
        os.makedirs(obj_dir)
    state_dir = get_state_dir()
    update_mercurial_repo("hg", COMM_L10N, mozpath.join(state_dir, "comm-strings"))
    return obj_dir


def test_migration(cmd, obj_dir, to_test, references):
    """Test the given recipe.

    This creates a workdir by merging comm-strings-quarantine and the c-c source,
    to mimic comm-strings-quarantine after the patch to test landed.
    It then runs the recipe with a comm-strings-quarantine clone as localization, both
    dry and wet.
    It inspects the generated commits, and shows a diff between the merged
    reference and the generated content.
    The diff is intended to be visually inspected. Some changes might be
    expected, in particular when formatting of the en-US strings is different.
    """
    rv = 0
    migration_name = os.path.splitext(os.path.split(to_test)[1])[0]
    l10n_lib = os.path.abspath(os.path.dirname(os.path.dirname(to_test)))
    work_dir = mozpath.join(obj_dir, migration_name)

    paths = os.path.normpath(to_test).split(os.sep)
    # Migration modules should be in a sub-folder of l10n.
    migration_module = ".".join(paths[paths.index("l10n") + 1 : -1]) + "." + migration_name

    if os.path.exists(work_dir):
        shutil.rmtree(work_dir)
    os.makedirs(mozpath.join(work_dir, "reference"))
    l10n_toml = mozpath.join(cmd.topsrcdir, cmd.substs["MOZ_BUILD_APP"], "locales", "l10n.toml")
    pc = TOMLParser().parse(l10n_toml, env={"l10n_base": work_dir})
    pc.set_locales(["reference"])
    files = ProjectFiles("reference", [pc])
    for ref in references:
        if ref != mozpath.normpath(ref):
            cmd.log(
                logging.ERROR,
                "tb-fluent-migration-test",
                {
                    "file": to_test,
                    "ref": ref,
                },
                'Reference path "{ref}" needs to be normalized for {file}',
            )
            rv = 1
            continue
        full_ref = mozpath.join(work_dir, "reference", ref)
        m = files.match(full_ref)
        if m is None:
            raise ValueError(f"Bad reference path: {ref} - {full_ref}")
        m_c_path = m[1]
        g_s_path = mozpath.join(work_dir, "comm-strings", ref)
        resources = [
            b"" if not os.path.exists(f) else open(f, "rb").read() for f in (g_s_path, m_c_path)
        ]
        ref_dir = os.path.dirname(full_ref)
        if not os.path.exists(ref_dir):
            os.makedirs(ref_dir)
        open(full_ref, "wb").write(merge_channels(ref, resources))
    client = hglib.clone(
        source=mozpath.join(get_state_dir(), "comm-strings"),
        dest=mozpath.join(work_dir, "comm-strings"),
    )
    client.open()
    old_tip = client.tip().node
    run_migration = [
        cmd._virtualenv_manager.python_path,
        "-m",
        "fluent.migratetb.tool",
        "--locale",
        "en-US",
        "--reference-dir",
        mozpath.join(work_dir, "reference"),
        "--localization-dir",
        mozpath.join(work_dir, "comm-strings"),
        "--dry-run",
        migration_module,
    ]
    append_env = {"PYTHONPATH": l10n_lib}
    cmd.run_process(
        run_migration,
        append_env=append_env,
        cwd=work_dir,
        line_handler=print,
    )
    # drop --dry-run
    run_migration.pop(-2)
    cmd.run_process(
        run_migration,
        append_env=append_env,
        cwd=work_dir,
        line_handler=print,
    )
    tip = client.tip().node
    if old_tip == tip:
        cmd.log(
            logging.WARN,
            "tb-fluent-migration-test",
            {
                "file": to_test,
            },
            "No migration applied for {file}",
        )
        return rv
    for ref in references:
        diff_resources(
            mozpath.join(work_dir, "reference", ref),
            mozpath.join(work_dir, "comm-strings", "en-US", ref),
        )
    messages = [l.desc.decode("utf-8") for l in client.log(b"::%s - ::%s" % (tip, old_tip))]
    bug = re.search("[0-9]{5,}", migration_name)
    # Just check first message for bug number, they're all following the same pattern
    if bug is None or bug.group() not in messages[0]:
        rv = 1
        cmd.log(
            logging.ERROR,
            "tb-fluent-migration-test",
            {
                "file": to_test,
            },
            "Missing or wrong bug number for {file}",
        )
    if any("part {}".format(n + 1) not in msg for n, msg in enumerate(messages)):
        rv = 1
        cmd.log(
            logging.ERROR,
            "tb-fluent-migration-test",
            {
                "file": to_test,
            },
            'Commit messages should have "part {{index}}" for {file}',
        )
    return rv
