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

import argparse
import os
import sys
import time

from mozpack.copier import FileCopier, FileRegistry
from mozpack.errors import errors
from mozpack.files import BaseFile, FileFinder
from mozpack.manifests import InstallManifest

from mozbuild.action.util import log_build_task
from mozbuild.util import DefinesAction

COMPLETE = (
    "Elapsed: {elapsed:.2f}s; From {dest}: Kept {existing} existing; "
    "Added/updated {updated}; "
    "Removed {rm_files} files and {rm_dirs} directories."
)


def process_manifest(destdir, paths, track, no_symlinks=False, defines={}):

    if os.path.exists(track):
        # We use the same format as install manifests for the tracking
        # data.
        manifest = InstallManifest(path=track)
        remove_unaccounted = FileRegistry()
        dummy_file = BaseFile()

        finder = FileFinder(destdir, find_dotfiles=True)
        for dest in manifest._dests:
            for p, f in finder.find(dest):
                remove_unaccounted.add(p, dummy_file)

        remove_empty_directories = True
        remove_all_directory_symlinks = True

    else:
        # If tracking is enabled and there is no file, we don't want to
        # be removing anything.
        remove_unaccounted = False
        remove_empty_directories = False
        remove_all_directory_symlinks = False

    manifest = InstallManifest()
    for path in paths:
        manifest |= InstallManifest(path=path)

    copier = FileCopier()
    link_policy = "copy" if no_symlinks else "symlink"
    manifest.populate_registry(
        copier, defines_override=defines, link_policy=link_policy
    )
    with errors.accumulate():
        result = copier.copy(
            destdir,
            remove_unaccounted=remove_unaccounted,
            remove_all_directory_symlinks=remove_all_directory_symlinks,
            remove_empty_directories=remove_empty_directories,
        )

    if track:
        # We should record files that we actually copied.
        # It is too late to expand wildcards when the track file is read.
        manifest.write(path=track, expand_pattern=True)

    return result


def main(argv):
    parser = argparse.ArgumentParser(description="Process install manifest files.")

    parser.add_argument("destdir", help="Destination directory.")
    parser.add_argument("manifests", nargs="+", help="Path to manifest file(s).")
    parser.add_argument(
        "--no-symlinks",
        action="store_true",
        help="Do not install symbolic links. Always copy files",
    )
    parser.add_argument(
        "--track",
        metavar="PATH",
        required=True,
        help="Use installed files tracking information from the given path.",
    )
    parser.add_argument(
        "-D",
        action=DefinesAction,
        dest="defines",
        metavar="VAR[=VAL]",
        help="Define a variable to override what is specified in the manifest",
    )

    args = parser.parse_args(argv)

    start = time.monotonic()

    result = process_manifest(
        args.destdir,
        args.manifests,
        track=args.track,
        no_symlinks=args.no_symlinks,
        defines=args.defines,
    )

    elapsed = time.monotonic() - start

    print(
        COMPLETE.format(
            elapsed=elapsed,
            dest=args.destdir,
            existing=result.existing_files_count,
            updated=result.updated_files_count,
            rm_files=result.removed_files_count,
            rm_dirs=result.removed_directories_count,
        )
    )


if __name__ == "__main__":
    log_build_task(main, sys.argv[1:])
