#!/usr/bin/env python
"""Load a yaml file and save it formatted according to some rules"""
import argparse
import copy
import sys
import os
from pathlib import Path
from ruamel.yaml import YAML
from ruamel.yaml.scanner import ScannerError


def parse_cli():
    """Parse the cli args"""
    my_args = dict()
    parser = argparse.ArgumentParser(
        description="""Format yaml input file.
            By default, explicit_start is `On`, explicit_end is `Off`\
            and array elements are pushed inwards the start of the \
            matching sequence. Comments are preserved thanks to default \
            parsing mode `rt`.
        """
    )
    parser.add_argument(
        "-i",
        "--input",
        required=False,
        help="the file to parse, or STDIN if not specified",
    )
    parser.add_argument(
        "-t",
        "--typ",
        required=False,
        default="rt",
        help="the yaml parser mode. Can be `safe` or `rt`",
    )
    parser.add_argument(
        "-o",
        "--output",
        required=False,
        help="the name of the file to generate \
                              (same as input file if not specied, \
                                hence STDOUT if STDIN as input)",
    )
    parser.add_argument(
        "-n",
        "--no-explicit-start",
        action="store_true",
        help="by default, explicit start of the yaml doc \
                                is `On`, you can disable it with this option",
    )
    parser.add_argument(
        "-e",
        "--explicit-end",
        action="store_true",
        help="by default, explicit end of the yaml doc \
                                is `Off`, you can enable it with this option",
    )
    parser.add_argument(
        "-q",
        "--no-quotes-preserved",
        action="store_true",
        help="by default, quotes are preserved \
                                you can disable this with this option",
    )
    parser.add_argument(
        "-f",
        "--default-flow-style",
        action="store_true",
        help="enable the default flow style \
                                `Off` by default. In default flow style \
                                (with typ=`rt`), maps and lists are written \
                                like json",
    )
    parser.add_argument(
        "-d",
        "--no-dash-inwards",
        action="store_true",
        help="by default, dash are pushed inwards \
                                use `--no-dash-inwards` to have the dash \
                                start at the sequence level",
    )
    parser.add_argument(
        "-s",
        "--stdout",
        action="store_true",
        help="output is STDOUT whatever the value for \
                          input (-i) and output (-o)",
    )

    args = parser.parse_args()

    input_display_name = "STDIN"
    if args.input is None:
        my_args["input"] = None
    else:
        my_args["input"] = args.input
        input_display_name = my_args["input"]
    if args.stdout:
        my_args["output"] = None
    else:
        if args.output is not None and args.output != "STDOUT":
            my_args["output"] = args.output
        else:
            if args.output == "STDOUT":
                my_args["output"] = None
            else:
                if my_args["input"] is None:
                    my_args["output"] = None
                else:
                    my_args["output"] = args.input

    if args.typ not in ["safe", "rt"]:
        raise ValueError(
            "'%s' is not a valid value for option --typ. "
            "Allowed values are 'safe' and 'rt'" % args.type
        )
    my_args["typ"] = args.typ
    my_args["explicit_start"] = not args.no_explicit_start
    my_args["explicit_end"] = args.explicit_end
    my_args["default_flow_style"] = args.default_flow_style
    my_args["dash_inwards"] = not args.no_dash_inwards
    my_args["quotes_preserved"] = not args.no_quotes_preserved
    if my_args["output"] is not None:
        print(
            "Processing: input="
            + input_display_name
            + ", output="
            + my_args["output"]
            + ", typ="
            + my_args["typ"]
            + ", explicit_start="
            + str(my_args["explicit_start"])
            + ", explicit_end="
            + str(my_args["explicit_end"])
            + ", default_flow_style="
            + str(my_args["default_flow_style"])
            + ", quotes_preserved="
            + str(my_args["quotes_preserved"])
            + ", dash_inwards="
            + str(my_args["dash_inwards"])
        )
    return my_args


def strip_leading_double_space(stream):
    if stream.startswith("  "):
        stream = stream[2:]
    return stream.replace("\n  ", "\n")
    # you could also do that on a line by line basis
    # return "".join([s[2:] if s.startswith("  ") else s for s in stream.splitlines(True)])


def format_yaml(
    input_file,
    output_file,
    explicit_start=True,
    explicit_end=False,
    default_flow_style=False,
    dash_inwards=True,
    quotes_preserved=True,
    parsing_mode="rt",
):
    """
    Load a file and save it formated :
    :param input_file: the input file
    :param output_file: the output file
    :param explicit_start: write the start of the yaml doc even when there is \
                            only one done in the file
    :param default_flow_style: if False, block style will be used for nested \
                              arrays/maps
    :param dash_inwards: push dash inwards if True
    :param quotes_preserved: preserve quotes if True
    :param parsing_typ: safe or roundtrip (rt) more
    """
    yaml = YAML(typ=parsing_mode)
    yaml.width = 2048
    yaml.explicit_start = explicit_start
    yaml.explicit_end = explicit_end
    yaml.default_flow_style = default_flow_style
    yaml.preserve_quotes = quotes_preserved

    if dash_inwards:
        yaml.indent(mapping=2, sequence=4, offset=2)

    if input_file is not None:
        with open(input_file, "rt") as f_input:
            parsed = yaml.load_all(f_input.read())
    else:
        parsed = yaml.load_all(sys.stdin.read())
    ready_for_dump = []
    try:
        # Read the parsed content to force the scanner to issue errors if any
        for data in parsed:
            ready_for_dump.append(data)

    except ScannerError as e:
        print("Something is wrong in the input file, got error from Scanner")
        print(e)
        return
    yamkix_dump_all(ready_for_dump, yaml, dash_inwards, output_file)


def yamkix_dump_all(dump_me, yaml, dash_inwards, output_file):
    if output_file is None:
        out = sys.stdout

    # Clear the output file if it is a file and it exists
    if output_file is not None and os.path.isfile(output_file):
        of = open(output_file, "w")
        of.close()
    for doc in dump_me:
        if output_file is None:
            yamkix_dump_one(doc, yaml, dash_inwards, out)
        else:
            with open(output_file, "a") as out:
                yamkix_dump_one(doc, yaml, dash_inwards, out)


def yamkix_dump_one(single_item, yaml, dash_inwards, out):
    if dash_inwards and type(single_item).__name__ == "CommentedSeq":
        yaml.dump(single_item, out, transform=strip_leading_double_space)
    else:
        yaml.dump(single_item, out)


def main():
    """(re)format yaml"""
    parsed_args = parse_cli()
    format_yaml(
        input_file=parsed_args["input"],
        output_file=parsed_args["output"],
        explicit_start=parsed_args["explicit_start"],
        explicit_end=parsed_args["explicit_end"],
        default_flow_style=parsed_args["default_flow_style"],
        dash_inwards=parsed_args["dash_inwards"],
        quotes_preserved=parsed_args["quotes_preserved"],
        parsing_mode=parsed_args["typ"],
    )


if __name__ == "__main__":
    main()
