import argparse
import logging

from ..log import TRACE
from ..version import __version__


class LogLevel:
    ERROR = "error"
    INFO = "info"
    DEBUG = "debug"
    TRACE = "trace"


def main():
    parser = create_parser()
    args = parser.parse_args()

    setup_logging(args)

    if args.command == "dump":
        from .dump import dump_main

        dump_main(args)
    elif args.command == "restore":
        from .restore import restore_main

        restore_main(args)
    elif args.command == "schema":
        from .schema import schema_main

        schema_main(args)
    elif args.command == "schema-filter":
        from .schema_filter import filter_main

        filter_main(args)
    elif args.command == "transform":
        from .transform import transform_main

        transform_main(args)
    elif args.command == "transform-field":
        from .transform_field import transform_field_main

        transform_field_main(args)


def create_parser():
    parser = argparse.ArgumentParser(
        description="Capture, scrub, and restore subsets of PostgreSQL databases.",
        fromfile_prefix_chars="@",
        formatter_class=ArgumentFormatter,
    )
    update_help(parser)
    parser.add_argument(
        "--log-level",
        choices=[LogLevel.ERROR, LogLevel.INFO, LogLevel.DEBUG, LogLevel.TRACE],
        default=LogLevel.INFO,
        help="Log level (default: %(default)s).",
    )
    parser.add_argument(
        "-v",
        "--version",
        action="version",
        version=f"%(prog)s {__version__}",
        help="Show version and exit.",
    )

    subparsers = parser.add_subparsers(
        dest="command",
        title="subcommands",
        description="Provide one of the subcommands for more specific help.",
    )

    add_dump_command(subparsers)
    add_restore_command(subparsers)
    add_schema_command(subparsers)
    add_schema_filter_command(subparsers)
    add_transform_command(subparsers)
    add_transform_field_command(subparsers)

    return parser


def add_dump_command(subparsers):
    parser = subparsers.add_parser(
        "dump",
        description="Dump data from database.",
        formatter_class=ArgumentFormatter,
    )
    update_help(parser)
    parser.add_argument(
        "--include-schema",
        "--no-include-schema",
        action=NegateAction,
        nargs=0,
        default=False,
        help="Whether to include schema. Only compatible with SQL output.",
    )
    parser.add_argument(
        "-j",
        "--jobs",
        default=1,
        help="Number of workers (default: %(default)d).",
        type=int,
    )
    parser.add_argument(
        "-o",
        "--output",
        default="-",
        help="Path to output slice, or - for stdout (default: %(default)s).",
    )
    parser.add_argument(
        "--output-type", choices=["slice", "sql"], default="slice", help="Output type."
    )
    parser.add_argument(
        "--pepper", help="Pepper to use for transform. Autogenerated if not provided."
    )
    parser.add_argument("--transform", help="Path to transform config, or - for stdin")
    parser.add_argument(
        "-r",
        "--root",
        action="append",
        default=[],
        dest="roots",
        metavar=("TABLE", "CONDITION"),
        nargs=2,
        help="The ID of the root table and SQL condition. May be repeated.",
    )
    parser_required = parser.add_argument_group("required arguments")
    parser_required.add_argument(
        "-s", "--schema", required=True, help="Path to schema, or - for stdin."
    )


def add_restore_command(subparsers):
    parser = subparsers.add_parser(
        "restore", description="Restore data.", formatter_class=ArgumentFormatter
    )
    update_help(parser)
    parser.add_argument(
        "--disable-triggers",
        "--no-disable-triggers",
        action=NegateAction,
        nargs=0,
        default=False,
        help="Disable triggers, including foreign keys.",
    )
    parser.add_argument(
        "-j",
        "--jobs",
        default=1,
        help="Number of workers (default: %(default)d). Using more than one worker requires disabling transactions.",
        type=int,
    )
    parser.add_argument(
        "--include-schema",
        "--no-include-schema",
        default=False,
        help="Include schema (default: %(default)s).",
        action=NegateAction,
        nargs=0,
    )
    parser.add_argument(
        "-i",
        "--input",
        default="-",
        help="Path to slice, or - for stdin (default: %(default)s).",
    )
    parser.add_argument(
        "--transaction",
        "--no-transaction",
        default=True,
        help="Whether to run in a single transaction (default: %(default)s).",
        action=NegateAction,
        nargs=0,
    )


def add_schema_command(subparsers):
    parser = subparsers.add_parser(
        "schema",
        description="Collect schema from database.",
        formatter_class=ArgumentFormatter,
    )
    update_help(parser)
    parser.add_argument(
        "-o",
        "--output",
        default="-",
        help="Path to output schema, or - for stdout (default: %(default)s).",
    )


def add_schema_filter_command(subparsers):
    parser = subparsers.add_parser("schema-filter")
    parser.add_argument("-i", "--input", default="-", help="Input")
    parser.add_argument("-o", "--output", default="-", help="Output")

    subsubparsers = parser.add_subparsers(dest="subcommand", title="subcommands")
    children_parser = subsubparsers.add_parser(
        "children",
        description="Include children, recursively",
        formatter_class=ArgumentFormatter,
    )
    update_help(children_parser)
    children_parser.add_argument(
        "--parents",
        default="none",
        choices=["none", "parents", "intelligent"],
        action=NegateAction,
        nargs=0,
    )
    children_parser.add_argument("table", nargs="*")


def add_transform_command(subparsers):
    parser = subparsers.add_parser(
        "transform", description="Transform slice", formatter_class=ArgumentFormatter
    )
    update_help(parser)
    parser.add_argument("--transform", required=True)


def add_transform_field_command(subparsers):
    parser = subparsers.add_parser(
        "transform-field",
        description="Transform field",
        formatter_class=ArgumentFormatter,
    )
    update_help(parser)
    parser.add_argument("--pepper", help="Pepper.")
    parser.add_argument("--transform", required=True)
    parser.add_argument("field")


def setup_logging(args):
    logging.addLevelName(TRACE, "TRACE")
    logging.basicConfig(format="[%(levelname)-8s] %(message)s")
    if args.log_level == LogLevel.ERROR:
        logging.getLogger().setLevel(logging.ERROR)
    elif args.log_level == LogLevel.INFO:
        logging.getLogger().setLevel(logging.INFO)
    elif args.log_level == LogLevel.DEBUG:
        logging.getLogger().setLevel(logging.DEBUG)
    elif args.log_level == LogLevel.TRACE:
        logging.getLogger().setLevel(TRACE)


class ArgumentFormatter(argparse.RawDescriptionHelpFormatter):
    def __init__(self, prog):
        super().__init__(prog, max_help_position=100, width=100)


class NegateAction(argparse.Action):
    def __call__(self, parser, ns, values, option):
        setattr(ns, self.dest, option[2:4] != "no")


def update_help(parser):
    parser._actions[-1].help = "Show this help message and exit."
