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

import argparse
import os
import subprocess
import sys
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(
        portage._unicode_encode(
            src_pkgs_index, encoding=portage._encodings["fs"], errors="strict"
        ),
        encoding=portage._encodings["repo.content"],
        errors="strict",
    )

    outfile = portage.util.atomic_ofstream(
        os.path.join(binhost_dir, "Packages"),
        encoding=portage._encodings["repo.content"],
        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))
