#!/usr/bin/env python3
import argparse
import sys

from tfcommander.__about__ import __version__
from tfcommander.config import add_alias, list_aliases, remove_alias
from tfcommander.consul import deregister_service, list_services, register_service
from tfcommander.exceptions import TFCommanderError
from tfcommander.traefik import list_middlewares, list_routers

EXIT_OK = 0
EXIT_GENERAL = 1
EXIT_VALIDATION = 2


def _build_parser() -> argparse.ArgumentParser:
    epilog_text = """
Examples:
  tf alias list
  tf alias add jim 192.168.19.7

  tf register jim mysvc MyService 192.168.19.4 3000 my.example.com
  tf register jim mysvc MyService 192.168.19.4 3000 my.example.com --middlewares auth@file
  tf register jim proxmox Proxmox 192.168.19.10 8006 prox.example.com --https-insecure
  tf register jim proxmox Proxmox 192.168.19.10 8006 prox.example.com --disable-tls
  tf register jim uploads Chibisafe 192.168.6.3 24424 files.domain.tld --servers-transport long-upload@file
  tf register jim mysvc MyService 192.168.19.4 3000 my.example.com --resolver letsencrypt
  tf register jim mysvc MyService 192.168.19.4 3000 --custom-rule "PathPrefix(\\`/api\\`)"
"""

    parser = argparse.ArgumentParser(
        description="Consul-Traefik Management CLI.",
        epilog=epilog_text,
        formatter_class=argparse.RawDescriptionHelpFormatter,
    )
    subparsers = parser.add_subparsers(dest="command")

    reg_parser = subparsers.add_parser("register", help="Register a service")
    reg_parser.add_argument("public_ip")
    reg_parser.add_argument("service_id")
    reg_parser.add_argument("service_name")
    reg_parser.add_argument("address")
    reg_parser.add_argument("port", type=int)
    reg_parser.add_argument("domains", nargs="*")
    reg_parser.add_argument("--middlewares", nargs="*")
    reg_parser.add_argument("--https-insecure", action="store_true")
    reg_parser.add_argument("--disable-tls", action="store_true")
    reg_parser.add_argument("--custom-rule", help="Custom Traefik routing rule")
    reg_parser.add_argument(
        "--servers-transport",
        help="Traefik serversTransport ref, e.g. large-upload-transport@file",
    )
    reg_parser.add_argument(
        "--resolver",
        help="TLS cert resolver name. Defaults to 'myresolver' if not specified.",
    )
    reg_parser.add_argument(
        "--traefik-port",
        type=int,
        default=None,
        help="Traefik API port (default: 8087)",
    )

    dereg_parser = subparsers.add_parser("deregister", help="Deregister a service")
    dereg_parser.add_argument("public_ip")
    dereg_parser.add_argument("service_id")

    list_parser = subparsers.add_parser("list", help="List Consul services")
    list_parser.add_argument("public_ip")

    list_tr_parser = subparsers.add_parser(
        "list-traefik", help="List Traefik routers"
    )
    list_tr_parser.add_argument("public_ip")
    list_tr_parser.add_argument(
        "--traefik-port",
        type=int,
        default=None,
        help="Traefik API port (default: 8087)",
    )

    list_mw_parser = subparsers.add_parser(
        "list-middlewares", help="List Traefik middlewares"
    )
    list_mw_parser.add_argument("public_ip")
    list_mw_parser.add_argument(
        "--traefik-port",
        type=int,
        default=None,
        help="Traefik API port (default: 8087)",
    )

    alias_parser = subparsers.add_parser("alias", help="Manage host aliases")
    alias_sub = alias_parser.add_subparsers(dest="alias_command")
    alias_add = alias_sub.add_parser("add", help="Add an alias")
    alias_add.add_argument("name")
    alias_add.add_argument("ip")
    alias_rm = alias_sub.add_parser("remove", help="Remove an alias")
    alias_rm.add_argument("name")
    alias_sub.add_parser("list", help="List all aliases")

    subparsers.add_parser("version", help="Show version")

    return parser


def _handle_alias(args: argparse.Namespace) -> int:
    if args.alias_command == "add":
        add_alias(args.name, args.ip)
    elif args.alias_command == "remove":
        try:
            remove_alias(args.name)
        except KeyError as e:
            print(e)
            return EXIT_GENERAL
    elif args.alias_command == "list":
        list_aliases()
    else:
        print("Invalid alias subcommand. Use: add, remove, or list.")
        return EXIT_GENERAL
    return EXIT_OK


def _handle_register(args: argparse.Namespace) -> int:
    if not args.domains and not args.custom_rule:
        print("Error: Must provide at least one domain or --custom-rule.")
        return EXIT_VALIDATION

    register_service(
        public_ip=args.public_ip,
        service_id=args.service_id,
        service_name=args.service_name,
        address=args.address,
        port=args.port,
        domains=args.domains,
        middlewares=args.middlewares,
        https_insecure=args.https_insecure,
        custom_rule=args.custom_rule,
        disable_tls=args.disable_tls,
        servers_transport=args.servers_transport,
        resolver=args.resolver,
    )
    return EXIT_OK


def main() -> None:
    parser = _build_parser()
    args = parser.parse_args()

    if not args.command:
        parser.print_help()
        sys.exit(EXIT_GENERAL)

    try:
        if args.command == "version":
            print(f"version: {__version__}")
            sys.exit(EXIT_OK)

        if args.command == "alias":
            sys.exit(_handle_alias(args))

        if args.command == "register":
            sys.exit(_handle_register(args))

        if args.command == "deregister":
            deregister_service(args.public_ip, args.service_id)

        if args.command == "list":
            list_services(args.public_ip)

        if args.command == "list-traefik":
            port_kwargs = {}
            if getattr(args, "traefik_port", None):
                port_kwargs["api_port"] = args.traefik_port
            list_routers(args.public_ip, **port_kwargs)

        if args.command == "list-middlewares":
            port_kwargs = {}
            if getattr(args, "traefik_port", None):
                port_kwargs["api_port"] = args.traefik_port
            list_middlewares(args.public_ip, **port_kwargs)

    except TFCommanderError as e:
        print(f"Error: {e}")
        sys.exit(EXIT_GENERAL)
    except KeyboardInterrupt:
        print("\nAborted.")
        sys.exit(130)


if __name__ == "__main__":
    main()
