#!/usr/bin/env python3

# SPDX-FileCopyrightText: 2009 Fermi Research Alliance, LLC
# SPDX-License-Identifier: Apache-2.0

# TODO: should this file be deprecated?
"""fact_chown

Change the ownership of frontend directories to the specified user.
This was needed when the Factory was running using multiple Unix accounts.
"""

import argparse
import grp
import logging
import os
import pwd
import subprocess
import sys
import tarfile

from contextlib import closing  # python 2.6 tarfile does not support with

import htcondor

from glideinwms.creation.lib import factoryXmlConfig


def parse_opts():
    """Parse the command-line options for changing ownership.

    Returns:
        argparse.Namespace: An object containing the parsed options. Expected options include:
            --user: The user to use when changing file ownership.
            --group: The group to use when changing file ownership.
            --backup: If set, back up the directories before changing ownership.
            --skipchown: If set, do not change the ownership of log file directories and log files.
            --skipfixqueue: If set, do not fix Condor job_queue files.
            --debug: Enable debug logging.
            --test: Run the script in test mode without changing permissions.

    Raises:
        SystemExit: If the required options --user or --group are missing.
    """
    description = "Change the ownership of frontend directories to the specified user\n\n"

    parser = argparse.ArgumentParser(description=description)

    parser.add_argument("--user", type=str, action="store", dest="user", help="User to use when chowning the files")

    parser.add_argument("--group", type=str, action="store", dest="group", help="Group to use when cowning the files")

    parser.add_argument(
        "--backup",
        action="store_true",
        dest="backup",
        default=False,
        help="Back up the directories into the cwd before chwon everything",
    )

    parser.add_argument(
        "--skipchown",
        action="store_true",
        dest="skipchown",
        default=False,
        help="Do not change ownership of logfiles directories and logfiles",
    )

    parser.add_argument(
        "--skipfixqueue",
        action="store_true",
        dest="skipfixqueue",
        default=False,
        help="Do not fix condor job_queue files",
    )

    parser.add_argument("--debug", action="store_true", dest="debug", default=False, help="Enable debug logging")

    parser.add_argument(
        "--test", action="store_true", dest="test", default=False, help="Run the script but do not change permissions"
    )

    options = parser.parse_args()

    if options.user is None:
        logging.error('Missing required option "--user"')
        sys.exit(1)

    if options.group is None:
        logging.error('Missing required option "--group"')
        sys.exit(1)

    # Initialize logging
    if options.debug:
        logging.basicConfig(format="%(levelname)s: %(message)s", level=logging.DEBUG)
    else:
        logging.basicConfig(level=logging.INFO)

    return options


# pylint: disable=no-member
def backup_dir(output_filename, source_dir):
    """Backup the specified directory into a tar.gz archive.

    Args:
        output_filename (str): The filename for the backup archive.
        source_dir (str): The directory to back up.
    """
    logging.info(f"Backing up {source_dir} into {output_filename}")
    with closing(tarfile.open(output_filename, "w:gz")) as tar:
        tar.add(source_dir, arcname=os.path.basename(source_dir))


def mychown(path, uid, gid, fuid, test):
    """Change the owner of the given path to the specified user and group.

    This function checks the current owner of the file/directory at 'path'.
    If the current owner does not match 'fuid', a warning is issued and the
    ownership is not changed. If test mode is enabled, only a message is printed
    and no change is made.

    Args:
        path (str): The path to the file or directory.
        uid (int): The new user ID.
        gid (int): The new group ID.
        fuid (int): The expected current Frontend user ID.
        test (bool): If True, do not change ownership; only log the action.
    """
    cid = os.stat(path).st_uid  # current id
    if cid != fuid:
        logging.warning(f"Not changing {path} since it's owner id is not {fuid}")
    if test:
        logging.info("Test mode: not changing %s" % path)
    else:
        os.chown(path, uid, gid)


def chown_dir(path, uid, gid, fuid, test):
    """Recursively change ownership of a directory and its contents.

    This function changes the ownership of the directory at 'path' and all
    its subdirectories and files to the specified uid and gid. It only changes
    ownership if the current owner matches 'fuid'. In test mode, no changes are made.

    Args:
        path (str): The directory path to process.
        uid (int): The new user ID.
        gid (int): The new group ID.
        fuid (int): The expected current user ID.
        test (bool): If True, only log actions without performing them.
    """
    mychown(os.path.dirname(path), uid, gid, fuid, test)
    mychown(path, uid, gid, fuid, test)
    for dirpath, dirnames, filenames in os.walk(path):
        try:
            target = None
            for dname in dirnames:
                target = os.path.join(dirpath, dname)
                mychown(target, uid, gid, fuid, test)
            for fname in filenames:
                target = os.path.join(dirpath, fname)
                mychown(target, uid, gid, fuid, test)
        except OSError:
            logging.warning("Cannot chown %s since the file does not exist anymore" % target)


def fix_jobqueue(jobqueue_file, user, options):
    """Fix the owner in the HTCondor job_queue file.

    This function replaces the owner name in the job queue file (job_queue.log by default) with the specified
    user. Optionally, a backup of the file is created before modification. In test mode,
    the function logs the intended action without applying changes.

    Args:
        jobqueue_file (str): The path to the Condor job queue file.
        user (str): The username to set as the new owner.
        options (argparse.Namespace): Parsed command-line options.
    """
    if options.backup:
        backup_name = os.path.dirname(jobqueue_file).split("/")[-2] + "_" + os.path.basename(jobqueue_file)
        backup_dir(backup_name + ".tar.gz", jobqueue_file)
    if options.test:
        logging.info("Test mode: not fixing %s" % jobqueue_file)
    else:
        logging.info("Fixing %s" % jobqueue_file)
        cmd = f"sed -i.bk -e 's/103 \\([-0-9.]*\\) Owner \".*\"/103 \\1 Owner \"'{user}'\"/' {jobqueue_file}"
        logging.debug("Executing: %s" % cmd)
        subprocess.call(cmd, shell=True)
        logging.info(
            "Please open %s and check that owner is %s on lines like '103 040087.-1 Owner \"%s\"'"
            % (jobqueue_file, user, user)
        )


def main():
    """Main function to change ownership of frontend directories and fix job queue files.

    The function loads the configuration file, retrieves the necessary user and group IDs,
    and then changes the ownership of directories as well as fixing the Condor job queue files
    if required.
    """
    options = parse_opts()

    # Factory side
    conf_file = "/etc/gwms-factory/glideinWMS.xml"  # TODO
    logging.info("Loading configuration file %s" % conf_file)
    conf = factoryXmlConfig.parse(conf_file)
    uid = pwd.getpwnam(options.user).pw_uid
    gid = grp.getgrnam(options.group)[2]

    # pylint: disable=maybe-no-member
    dir_dicts = [conf.get_client_log_dirs(), conf.get_client_proxy_dirs()]

    if not options.skipchown:
        for client_dir_dict in dir_dicts:
            for fe_user, fe_client_dir in client_dir_dict.items():
                logging.info(f"Working on {fe_client_dir}: changing ownership from {fe_user} to {options.user}")
                if options.backup:
                    logging.info("Backing it up in the current directory first")
                    backup_dir(fe_user + ".tar.gz", fe_client_dir)
                fuid = pwd.getpwnam(fe_user).pw_uid
                chown_dir(fe_client_dir, uid, gid, fuid, options.test)

    # Condor side
    if not options.skipfixqueue:
        spooldir = htcondor.param["SPOOL"]
        if "JOBQUEUE" not in htcondor.param:
            jobqueue_file = os.path.join(spooldir, "job_queue.log")
        else:
            jobqueue_file = htcondor.param["JOBQUEUE"]
        fix_jobqueue(jobqueue_file, options.user, options)
        for i in range(1, 10):
            try:
                jobqueue_file = htcondor.param["SCHEDDGLIDEINS%s.JOB_QUEUE_LOG" % i]
            except KeyError:
                logging.warning("Cannot find 'SCHEDDGLIDEINS%s.JOB_QUEUE_LOG'. Skipping it." % i)
                continue
            fix_jobqueue(jobqueue_file, options.user, options)


if __name__ == "__main__":
    main()
