#!/usr/bin/python3

import argparse
import os
import sys

import ssg.build_yaml
import ssg.controls
import ssg.environment
import ssg.products
import ssg.build_stig
from utils.srg_utils import get_rule_dir_json, fix_changed_text
from utils.srg_utils.yaml import update_row

SSG_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
RULES_JSON = os.path.join(SSG_ROOT, "build", "rule_dirs.json")
BUILD_CONFIG = os.path.join(SSG_ROOT, "build", "build_config.yml")


def _parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        description="Import a DISA STIG XML file to the policy specific content files.")
    parser.add_argument("input", action="store",
                        help="The path to the STIG XML file.")
    parser.add_argument("-r", "--root", type=str, action="store", default=SSG_ROOT,
                        help=f"Path to SSG root directory (defaults to {SSG_ROOT})")
    parser.add_argument("-c", "--control", required=True,
                        help="Name of the policy to use.")
    parser.add_argument("-j", "--json", type=str, action="store", default=RULES_JSON,
                        help=f"Path to the rules_dir.json (defaults to {RULES_JSON})")
    parser.add_argument("-p", "--product", type=str, action="store", required=True,
                        help="What product id to use.")
    parser.add_argument("-b", "--build-config-yaml", default=BUILD_CONFIG,
                        help="YAML file with information about the build configuration. "
                        f"Defaults to {BUILD_CONFIG}")
    parser.add_argument('--changed-name', '-n', type=str, action="store",
                        help="The name that DISA uses for the product. Defaults to RHEL 9",
                        default="RHEL 9")
    return parser.parse_args()


def _get_env_yaml(ssg_root: str, product: str, build_config_yaml: str) -> dict:
    product_dir = os.path.join(ssg_root, "products", product)
    product_yaml_path = os.path.join(product_dir, "product.yml")
    env_yaml = ssg.environment.open_environment(
        build_config_yaml, product_yaml_path, os.path.join(ssg_root, "product_properties"))
    return env_yaml


def _get_controls(control_name, ssg_root, env_yaml):
    control_manager = ssg.controls.ControlsManager(os.path.join(ssg_root, "controls"), env_yaml)
    control_manager.load()
    controls = control_manager.get_all_controls_dict(control_name)
    return controls


def _get_rule_obj(control, rule_dir_json):
    rule_id = control.rules[0]
    rule_obj = rule_dir_json[rule_id]
    return rule_obj


def _get_rule(env_yaml, rule_obj):
    rule_path = os.path.join(rule_obj["dir"], "rule.yml")
    rule = ssg.build_yaml.Rule.from_yaml(rule_path, env_yaml)
    rule.load_policy_specific_content(rule_path, env_yaml)
    return rule


def main() -> int:
    args = _parse_args()
    build_config_yaml = args.build_config_yaml
    changed_name = args.changed_name
    env_yaml = _get_env_yaml(args.root, args.product, build_config_yaml)
    controls = _get_controls(args.control, args.root, env_yaml)
    rule_dir_json = get_rule_dir_json(args.json)
    srgs = ssg.build_stig.parse_srgs(args.input)

    for stig_id, stig_rule in srgs.items():
        control = controls[stig_id]
        if control is None or control.rules is None or len(control.rules) != 1:
            print(f"Warning: Unable to update {stig_id} since it doesn't have exactly one "
                  f"rule.", file=sys.stderr)
            continue

        rule_obj = _get_rule_obj(control, rule_dir_json)
        rule = _get_rule(env_yaml, rule_obj)

        stig_srg_requirement = stig_rule['title']
        rule_srg_requirement = rule.policy_specific_content.get('srg_requirement')
        if rule_srg_requirement != stig_srg_requirement:
            changed_fixtext = fix_changed_text(stig_srg_requirement, changed_name)
            update_row(changed_fixtext, stig_srg_requirement, rule_obj, 'srg_requirement')

        stig_fix_text = stig_rule['fixtext']
        rule_fixtext = rule.policy_specific_content.get('fixtext')
        if rule_fixtext != stig_fix_text:
            changed_fixtext = fix_changed_text(stig_fix_text, changed_name)
            changed_fixtext = changed_fixtext.replace(f'auid>={env_yaml["uid_min"]}',
                                                      'auid&gt;={{{ uid_min }}}')
            update_row(changed_fixtext, rule_fixtext, rule_obj, 'fixtext')

        stig_checktext = stig_rule['check']
        rule_checktext = rule.policy_specific_content.get('checkfix')
        if rule_checktext != stig_checktext:
            changed_checktext = fix_changed_text(stig_checktext, changed_name)
            changed_checktext = changed_checktext.replace(f'auid>={env_yaml["uid_min"]}',
                                                          'auid&gt;={{{ uid_min }}}')
            update_row(changed_checktext, rule_checktext, rule_obj, 'checktext')

        stig_vuln_discussion = stig_rule['vuln_discussion']
        rule_vuln_discussion = rule.policy_specific_content.get('vuldiscussion')
        if stig_vuln_discussion != rule_vuln_discussion:
            changed_vuln_discussion = fix_changed_text(stig_vuln_discussion, changed_name)
            update_row(changed_vuln_discussion, rule_vuln_discussion, rule_obj,
                       'vuldiscussion')

    return 0


if __name__ == "__main__":
    raise SystemExit(main())
