# 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/.

from mach.decorators import Command, CommandArgument

LICENSE_HEADER = """# 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/.
"""

GENERATED_HEADER = """
### This file was AUTOMATICALLY GENERATED by `./mach update-glean-tags`
### DO NOT edit it by hand.
"""


@Command(
    "data-review",
    category="misc",
    description="Generate a skeleton data review request form for a given bug's data",
)
@CommandArgument(
    "bug", default=None, nargs="?", type=str, help="bug number or search pattern"
)
def data_review(command_context, bug=None):
    # Get the metrics_index's list of metrics indices
    # by loading the index as a module.
    import sys
    from os import path

    sys.path.append(path.join(path.dirname(__file__), path.pardir))
    from pathlib import Path

    from glean_parser import data_review
    from metrics_index import metrics_yamls

    return data_review.generate(
        bug, [Path(command_context.topsrcdir) / x for x in metrics_yamls]
    )


@Command(
    "perf-data-review",
    category="misc",
    description="Generate a skeleton performance data review request form for a given bug's data",
)
@CommandArgument(
    "bug", default=None, nargs="?", type=str, help="bug number or search pattern"
)
def perf_data_review(command_context, bug=None):
    # Get the metrics_index's list of metrics indices
    # by loading the index as a module.
    import sys
    from os import path

    sys.path.append(path.join(path.dirname(__file__), path.pardir))
    from metrics_index import metrics_yamls

    sys.path.append(path.dirname(__file__))
    from pathlib import Path

    import perf_data_review

    return perf_data_review.generate(
        bug, [Path(command_context.topsrcdir) / x for x in metrics_yamls]
    )


@Command(
    "update-glean-tags",
    category="misc",
    description=(
        "Creates a list of valid glean tags based on in-tree bugzilla component definitions"
    ),
)
def update_glean_tags(command_context):
    from pathlib import Path

    import yaml
    from mozbuild.backend.configenvironment import ConfigEnvironment
    from mozbuild.frontend.reader import BuildReader

    config = ConfigEnvironment(
        command_context.topsrcdir,
        command_context.topobjdir,
        defines=command_context.defines,
        substs=command_context.substs,
    )

    reader = BuildReader(config)
    bug_components = set()
    for p in reader.read_topsrcdir():
        if p.get("BUG_COMPONENT"):
            bug_components.add(p["BUG_COMPONENT"])

    tags_filename = (Path(__file__).parent / "../tags.yaml").resolve()

    tags = {"$schema": "moz://mozilla.org/schemas/glean/tags/1-0-0"}
    for bug_component in bug_components:
        product = bug_component.product.strip()
        component = bug_component.component.strip()
        tags["{} :: {}".format(product, component)] = {
            "description": "The Bugzilla component which applies to this object."
        }

    open(tags_filename, "w").write(
        "{}\n{}\n\n".format(LICENSE_HEADER, GENERATED_HEADER)
        + yaml.dump(tags, width=78, explicit_start=True)
    )


def replace_in_file(path, pattern, replace):
    """
    Replace `pattern` with `replace` in the file `path`.
    The file is modified on disk.

    Returns `True` if exactly one replacement happened.
    `False` otherwise.
    """

    import re

    with open(path, "r+") as file:
        data = file.read()
        data, subs_made = re.subn(pattern, replace, data, flags=re.MULTILINE)

        file.seek(0)
        file.write(data)
        file.truncate()

        if subs_made != 1:
            return False

    return True


def replace_in_file_or_die(path, pattern, replace):
    """
    Replace `pattern` with `replace` in the file `path`.
    The file is modified on disk.

    If not exactly one occurrence of `pattern` was replaced it will exit with exit code 1.
    """

    import sys

    success = replace_in_file(path, pattern, replace)
    if not success:
        print(f"ERROR: Failed to replace one occurrence in {path}")
        print(f"  Pattern: {pattern}")
        print(f"  Replace: {replace}")
        print("File was modified. Check the diff.")
        sys.exit(1)


@Command(
    "update-glean",
    category="misc",
    description="Update Glean to the given version",
)
@CommandArgument("version", help="Glean version to upgrade to")
def update_glean(command_context, version):
    import textwrap
    from pathlib import Path

    topsrcdir = Path(command_context.topsrcdir)

    replace_in_file_or_die(
        topsrcdir / "build.gradle",
        r'gleanVersion = "[0-9.]+"',
        f'gleanVersion = "{version}"',
    )
    replace_in_file_or_die(
        topsrcdir / "toolkit" / "components" / "glean" / "Cargo.toml",
        r'^glean = "[0-9.]+"',
        f'glean = "{version}"',
    )
    replace_in_file_or_die(
        topsrcdir / "toolkit" / "components" / "glean" / "api" / "Cargo.toml",
        r'^glean = "[0-9.]+"',
        f'glean = "{version}"',
    )
    replace_in_file_or_die(
        topsrcdir / "gfx" / "wr" / "webrender" / "Cargo.toml",
        r'^glean = "[0-9.]+"',
        f'glean = "{version}"',
    )
    replace_in_file_or_die(
        topsrcdir / "python" / "sites" / "mach.txt",
        r"glean-sdk==[0-9.]+",
        f"glean-sdk=={version}",
    )

    instructions = f"""
    We've edited most of the necessary files to require Glean SDK {version}.

    You will have to edit the following files yourself:

        gfx/wr/wr_glyph_rasterizer/Cargo.toml

    Then, to ensure Glean and Firefox's other Rust dependencies are appropriately vendored,
    please run the following commands:

        cargo update -p glean
        ./mach vendor rust --ignore-modified

    `./mach vendor rust` may identify version mismatches.
    Please consult the Updating the Glean SDK docs for assistance:
    https://firefox-source-docs.mozilla.org/toolkit/components/glean/dev/updating_sdk.html

    The Glean SDK is already vetted and no additional vetting for it is necessary.
    To prune the configuration file after vendoring run:

        ./mach cargo vet prune

    Then, to update webrender which independently relies on the Glean SDK, run:

        cd gfx/wr
        cargo update -p glean

    Then, to ensure all is well, build Firefox and run the FOG tests.
    Instructions can be found here:
    https://firefox-source-docs.mozilla.org/toolkit/components/glean/dev/testing.html
    """

    print(textwrap.dedent(instructions))
