#!/usr/bin/env python
"""Converts file format of input ontology and write it to output file(s).
"""
import argparse
import warnings

from rdflib.util import guess_format

from ontopy import get_ontology
from ontopy.utils import annotate_source, rename_iris


def main(argv: list = None):
    """Main run function.

    Parameters:
        argv: List of arguments, similar to `sys.argv[1:]`.
            Mainly for testing purposes, since it allows one to invoke the tool
            manually / through Python.

    """
    # pylint: disable=too-many-branches,too-many-statements,invalid-name
    # pylint: disable=too-many-locals
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("input", help="IRI/file to OWL source.")
    parser.add_argument("output", help="Output file name.")
    parser.add_argument(
        "--input-format",
        "-f",
        help=(
            "Input format (default is to infer from input).  Available "
            'formats: "xml" (rdf/xml), "n3", "nt", "trix", "rdfa"'
        ),
    )
    parser.add_argument(
        "--output-format",
        "-F",
        help=(
            "Output format (default is to infer from output.  Available "
            'formats: "xml" (rdf/xml), "n3", "turtle", "nt", "pretty-xml", '
            '"trix"'
        ),
    )
    parser.add_argument(
        "--output-dir",
        "-d",
        default=".",
        help=(
            "Output directory.  If `output` is a relative path, it will be "
            "relative to this directory."
        ),
    )
    parser.add_argument(
        "--overwrite",
        "-w",
        action="store_true",
        help=(
            "Whether to remove `output` if it already exists. "
            "The default is to append to it."
        ),
    )
    parser.add_argument(
        "--no-catalog",
        "-n",
        action="store_false",
        dest="url_from_catalog",
        default=None,
        help="Whether to not read catalog file even if it exists.",
    )
    parser.add_argument(
        "--reasoner",
        "--infer",
        "-i",
        nargs="?",
        const="FaCT++",
        choices=["HermiT", "Pellet", "FaCT++"],
        metavar="NAME",
        help=(
            "Add additional relations inferred by the reasoner.  Supported "
            'reasoners are "FaCT++" (default), "HermiT" and "Pellet".'
        ),
    )
    parser.add_argument(
        "--no-infer-imported",
        "--no-reason-imported",
        action="store_true",
        help="Do not infer imported ontologies.",
    )
    parser.add_argument(
        "--base-iri",
        "-b",
        help=(
            "Base iri of inferred ontology. The default is the base iri of "
            'the input ontology with "-inferred" appended to it. Used '
            "together with --reasoner."
        ),
    )
    parser.add_argument(
        "--quiet",
        "-q",
        action="store_true",
        help="Don't print a lot of stuff to stdout during reasoning.",
    )
    parser.add_argument(
        "--recursive",
        "-r",
        action="store_true",
        help=(
            "Whether to also convert imported ontologies recursively using "
            "rdflib. The output is written to a directory structure matching "
            "the input. "
            "This option requires Protege catalog files to be present. "
            "It cannot be combined with other options like --squash, "
            "--inferred, --annotate-source, and --rename-iris."
        ),
    )
    parser.add_argument(
        "--squash",
        "-s",
        action="store_true",
        help=(
            "Whether to also squash imported ontologies into a single output "
            "file. Cannot be combined with --recursive."
        ),
    )
    parser.add_argument(
        "--annotate-source",
        "-a",
        action="store_true",
        help=(
            "Whether to annotate all entities with be base IRI of the source "
            "ontology using `dcterms:source` relations.  This is contextual "
            "information that is otherwise lost when ontologies are inferred "
            "and/or squashed."
        ),
    )
    parser.add_argument(
        "--rename-iris",
        "-R",
        nargs="?",
        const="prefLabel",
        metavar="ANNOTATION",
        help=(
            "For all entities that have the given annotation ('prefLabel' "
            "by default), change the name of the entity to the value of the "
            "annotation.\n"
            "For all changed entities, an `equivalentTo` annotation is "
            "added, referring to the old name.\n"
            "This option is useful to create a copy of an ontology with "
            "more human readable IRIs."
        ),
    )
    parser.add_argument(
        "--catalog-file",
        "-C",
        nargs="?",
        const="catalog-v001.xml",
        metavar="FILENAME",
        help='Whether to write catalog file. Defaults to "catalog-v001.xml".',
    )
    parser.add_argument(
        "--append-catalog",
        "-A",
        action="store_true",
        help="Whether to append to (possible) existing catalog file.",
    )

    args = parser.parse_args(args=argv)

    # Inferred default input and output file formats
    if args.input_format:
        input_format = args.input_format
    else:
        input_format = guess_format(args.input)

    if args.output_format:
        output_format = args.output_format
    else:
        output_format = guess_format(args.output)
    if not output_format:
        output_format = "xml"

    # Perform conversion
    with warnings.catch_warnings(record=True) as warnings_handle:
        warnings.simplefilter("always")

        onto = get_ontology(args.input).load(
            format=input_format,
            url_from_catalog=args.url_from_catalog,
        )

        if args.annotate_source:
            annotate_source(onto)

        if args.rename_iris:
            rename_iris(onto, args.rename_iris)

        if args.reasoner:
            include_imported = not args.no_infer_imported
            verbose = not args.quiet
            onto.sync_reasoner(
                reasoner=args.reasoner,
                include_imported=include_imported,
                debug=verbose,
            )

        onto.save(
            args.output,
            format=output_format,
            dir=args.output_dir,
            mkdir=True,
            overwrite=args.overwrite,
            recursive=args.recursive,
            squash=args.squash,
            write_catalog_file=bool(args.catalog_file),
            append_catalog=args.append_catalog,
            catalog_file=args.catalog_file,
        )

        for warning in warnings_handle:
            print(
                f"\033[93mWARNING\033[0m: [{warning.category.__name__}] "
                f"{warning.message}"
            )


if __name__ == "__main__":
    main()
