#!/usr/bin/env python3

import argparse
import dataclasses
import os
import shutil
import subprocess
import sys
import textwrap

from argparse import ArgumentParser, Namespace
from pathlib import Path

_SCRIPT_NAME = Path(sys.argv[0]).name
_SCRIPT_DIR = Path(sys.argv[0]).parent.resolve()
_TOP_LEVEL = Path(
    subprocess.check_output(["git", "rev-parse", "--show-toplevel"], text=True).strip()
)


class JqBinResolver:
    def __init__(self, parser: ArgumentParser) -> None:
        parser.add_argument("--jq-bin", help="jq executable")

    def resolve(self, args: Namespace) -> Path:
        res_str = args.jq_bin or shutil.which("jq")
        if res_str is None:
            print(
                textwrap.dedent(
                    """
                    jq not found.
                    Try setting --jq-bin or ensuring it's on your PATH.
                    On debian, it can be installed with:
                    apt install jq
                    """
                )
            )
            sys.exit(1)
        res = Path(res_str)
        if not os.access(res, os.X_OK):
            print(f"jq resolved to {res}, but it isn't an executable file")
            sys.exit(1)
        return res


class ChutneyBinResolver:
    def __init__(self, parser: ArgumentParser) -> None:
        parser.add_argument("--chutney-bin", help="chutney executable")

    def resolve(self, args: Namespace) -> Path:
        res_str = args.chutney_bin or shutil.which("chutney")
        if res_str is None:
            print(
                textwrap.dedent(
                    """
                    chutney (https://gitlab.torproject.org/tpo/core/chutney) not found.
                    Try setting --chutney-bin or ensuring it's on your PATH.
                    quick install:
                    python3 -m pip install git+https://gitlab.torproject.org/tpo/core/chutney.git
                    """
                )
            )
            sys.exit(1)
        res = Path(res_str)
        if not os.access(res, os.X_OK):
            print(f"chutney resolved to {res}, but it isn't an executable file")
            sys.exit(1)
        return res


class ArtiBinResolver:
    def __init__(self, parser: ArgumentParser) -> None:
        parser.add_argument("--arti-bin", help="arti executable")

    def resolve(self, args: Namespace) -> Path:
        res_str = args.arti_bin
        if res_str is None:
            for s in [
                "target/x86_64-unknown-linux-gnu/quicktest/arti",
                "target/quicktest/arti",
                "target/x86_64-unknown-linux-gnu/debug/arti",
                "target/debug/arti",
            ]:
                p = _TOP_LEVEL.joinpath(Path(s))
                if p.exists():
                    res_str = str(p)
                    break
        if res_str is None:
            print(
                textwrap.dedent(
                    """
                    arti client not found.
                    Try setting --arti-bin or building one in this repo:
                    cargo build --locked --profile=quicktest -p arti
                    """
                )
            )
            sys.exit(1)
        res = Path(res_str)
        if not os.access(res, os.X_OK):
            print(f"arti resolved to {res}, but it isn't an executable file")
            sys.exit(1)
        return res


class ChutneyDataDirResolver:
    def __init__(self, parser: ArgumentParser) -> None:
        parser.add_argument(
            "--chutney-data-dir", help="directory for chutney to put its data"
        )

    def resolve(self, args: Namespace) -> Path:
        return Path(
            args.chutney_data_dir or os.getenv("CHUTNEY_DATA_DIR") or os.getcwd()
        )


class ChutneyNetworkResolver:
    def __init__(self, parser: ArgumentParser) -> None:
        parser.add_argument(
            "--network", "-n", help="name or path of a chutney network to launch"
        )

    def resolve(self, args: Namespace) -> str:
        return args.network or str(_SCRIPT_DIR.joinpath("networks", "arti-ci"))


@dataclasses.dataclass
class Config:
    chutney: Path
    arti: Path
    jq: Path
    chutney_data_dir: Path
    network: str


class ConfigResolver:
    def __init__(self, parser: ArgumentParser) -> None:
        self._jq_resolver = JqBinResolver(parser)
        self._chutney_resolver = ChutneyBinResolver(parser)
        self._arti_resolver = ArtiBinResolver(parser)
        self._chutney_data_dir_resolver = ChutneyDataDirResolver(parser)
        self._network_resolver = ChutneyNetworkResolver(parser)

    def resolve(self, args: Namespace) -> Config:
        return Config(
            chutney=self._chutney_resolver.resolve(args),
            arti=self._arti_resolver.resolve(args),
            jq=self._jq_resolver.resolve(args),
            chutney_data_dir=self._chutney_data_dir_resolver.resolve(args),
            network=self._network_resolver.resolve(args),
        )


def _main() -> None:
    # Set up command-line parser, registering config options
    parser = argparse.ArgumentParser(
        prog=_SCRIPT_NAME, description="Configure a chutney testbed"
    )
    config_resolver = ConfigResolver(parser)
    args = parser.parse_args()
    config = config_resolver.resolve(args)

    with Path(_SCRIPT_DIR).joinpath("arti.run").open("w") as c:
        print(f'target="{config.network}"', file=c)
        print(f'jq_bin="{config.jq}"', file=c)
        print(f'chutney_bin="{config.chutney}"', file=c)
        print(f'export CHUTNEY_ARTI="{config.arti}"', file=c)
        print(f'export CHUTNEY_DATA_DIR="{config.chutney_data_dir}"', file=c)


if __name__ == "__main__":
    _main()
