#!/usr/bin/env python
# Copyright 2010-2014 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2

import locale
import os
import sys

if (
    sys.getfilesystemencoding().lower() != "utf-8"
    or locale.getpreferredencoding(False).lower() != "utf-8"
):
    os.environ["PYTHONUTF8"] = "1"
    os.execv(sys.executable, [sys.executable] + sys.argv)


import argparse
import subprocess
import textwrap

from urllib.parse import urlparse

from os import path as osp

if osp.isfile(
    osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), ".portage_not_installed")
):
    sys.path.insert(
        0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "lib")
    )
import portage

portage._internal_caller = True


def parse_args(argv):
    prog_name = os.path.basename(argv[0])
    usage = (
        prog_name
        + " [options] "
        + "<src_pkg_dir> <snapshot_dir> <snapshot_uri> <binhost_dir>"
    )

    prog_desc = (
        "This program will copy src_pkg_dir to snapshot_dir "
        + "and inside binhost_dir it will create a Packages index file "
        + "which refers to snapshot_uri. This is intended to solve race "
        + "conditions on binhosts as described at http://crosbug.com/3225."
    )

    usage += "\n\n"
    for line in textwrap.wrap(prog_desc, 70):
        usage += line + "\n"

    usage += "\n"
    usage += "Required Arguments:\n\n"
    usage += "  src_pkg_dir  - the source ${PKGDIR}\n"
    usage += "  snapshot_dir - destination snapshot " + "directory (must not exist)\n"
    usage += (
        "  snapshot_uri - URI which refers to "
        + "snapshot_dir from the\n"
        + "                 client side\n"
    )
    usage += (
        "  binhost_dir  - directory in which to "
        + "write Packages index with\n"
        + "                 snapshot_uri"
    )

    parser = argparse.ArgumentParser(usage=usage)
    parser.add_argument(
        "--hardlinks",
        help="create hardlinks (y or n, default is y)",
        choices=("y", "n"),
        default="y",
    )
    options, args = parser.parse_known_args(argv[1:])

    if len(args) != 4:
        parser.error(f"Required 4 arguments, got {len(args)}")

    return parser, options, args


def main(argv):
    parser, options, args = parse_args(argv)

    src_pkg_dir, snapshot_dir, snapshot_uri, binhost_dir = args
    src_pkgs_index = os.path.join(src_pkg_dir, "Packages")

    if not os.path.isdir(src_pkg_dir):
        parser.error(f"src_pkg_dir is not a directory: '{src_pkg_dir}'")

    if not os.path.isfile(src_pkgs_index):
        parser.error(
            "src_pkg_dir does not contain a " + f"'Packages' index: '{src_pkg_dir}'"
        )

    parse_result = urlparse(snapshot_uri)
    if not (parse_result.scheme and parse_result.netloc and parse_result.path):
        parser.error(f"snapshot_uri is not a valid URI: '{snapshot_uri}'")

    if os.path.isdir(snapshot_dir):
        parser.error(f"snapshot_dir already exists: '{snapshot_dir}'")

    dirname_ss_dir = os.path.dirname(snapshot_dir)
    try:
        os.makedirs(dirname_ss_dir)
    except OSError:
        pass
    if not os.path.isdir(dirname_ss_dir):
        parser.error(f"snapshot_dir parent could not be created: '{dirname_ss_dir}'")

    try:
        os.makedirs(binhost_dir)
    except OSError:
        pass
    if not os.path.isdir(binhost_dir):
        parser.error(f"binhost_dir could not be created: '{binhost_dir}'")

    cp_opts = "-RP"
    if options.hardlinks == "n":
        cp_opts += "p"
    else:
        cp_opts += "l"

    try:
        result = subprocess.run(["cp", cp_opts, src_pkg_dir, snapshot_dir])
    except OSError:
        result = None

    if result is None or result.returncode != 0:
        return 1

    infile = open(
        src_pkgs_index,
        encoding="utf-8",
        errors="strict",
    )

    outfile = portage.util.atomic_ofstream(
        os.path.join(binhost_dir, "Packages"),
        encoding="utf-8",
        errors="strict",
    )

    for line in infile:
        if line[:4] == "URI:":
            # skip existing URI line
            pass
        else:
            if not line.strip():
                # end of header
                outfile.write(f"URI: {snapshot_uri}\n\n")
                break
            outfile.write(line)

    for line in infile:
        outfile.write(line)

    infile.close()
    outfile.close()

    return os.EX_OK


if __name__ == "__main__":
    sys.exit(main(sys.argv))
