# SPDX-FileCopyrightText: AISEC Pentesting Team
#
# SPDX-License-Identifier: Apache-2.0

# pylint: disable=eval-used

import json
import random
import sys
from argparse import Namespace
from pathlib import Path

from gallia.command import AsyncScript
from gallia.transports.base import TargetURI
from gallia.transports.can import ISOTPTransport
from gallia.transports.tcp import TCPLineSepTransport
from gallia.uds.core.constants import UDSIsoServices
from gallia.uds.server import (
    DBUDSServer,
    ISOTPUDSServerTransport,
    RandomUDSServer,
    TCPUDSServerTransport,
    UDSServer,
    UDSServerTransport,
)

dynamic_attr_prefix = "dynamic_attr_"


class VirtualECU(AsyncScript):
    """Spawn a virtual ECU for testing purposes"""

    COMMAND = "vecu"
    SHORT_HELP = "spawn a virtual UDS ECU"
    EPILOG = "https://fraunhofer-aisec.github.io/gallia/uds/virtual_ecu.html"

    def configure_parser(self) -> None:
        self.parser.add_argument(
            "target",
            type=TargetURI,
        )

        sub_parsers = self.parser.add_subparsers(dest="cmd")
        sub_parsers.required = True

        db = sub_parsers.add_parser("db")
        db.add_argument(
            "path",
            type=Path,
        )
        db.add_argument("--ecu", type=str)
        db.add_argument("--properties", type=json.loads)
        # db.set_defaults(yolo=True)

        rng = sub_parsers.add_parser("rng")
        rng.add_argument(
            "--seed",
            default=random.randint(0, sys.maxsize),
            help="Set the seed of the internal random number generator. This supports reproducibility.",
        )

        # Expose all other parameters of the random UDS server
        tmp = RandomUDSServer(0)
        parent_attrs = True

        for v in tmp.__dict__:
            if parent_attrs:
                if v == "seed":
                    parent_attrs = False

                if v.startswith("use_default_response_if"):
                    self.parser.add_argument(f"--{v}", dest=f"{dynamic_attr_prefix}{v}")

                continue

            if not v.startswith("_"):
                rng.add_argument(f"--{v}", dest=f"{dynamic_attr_prefix}{v}")

    async def main(self, args: Namespace) -> None:
        cmd: str = args.cmd
        server: UDSServer

        if cmd == "db":
            server = DBUDSServer(args.path, args.ecu, args.properties)
        elif cmd == "rng":
            server = RandomUDSServer(args.seed)
        else:
            assert False

        for key, value in vars(args).items():
            if key.startswith(dynamic_attr_prefix) and value is not None:
                setattr(
                    server,
                    key[len(dynamic_attr_prefix) :],
                    eval(
                        value,
                        dict((service.name, service) for service in UDSIsoServices),
                    ),
                )

        target: TargetURI = args.target
        transport: UDSServerTransport

        if target.scheme == TCPLineSepTransport.SCHEME:
            transport = TCPUDSServerTransport(server, target)
        elif target.scheme == ISOTPTransport.SCHEME:
            transport = ISOTPUDSServerTransport(server, target)
        else:
            self.logger.error(
                f"Unsupported transport scheme! Use any of ["
                f"{TCPLineSepTransport.SCHEME}, {ISOTPTransport.SCHEME}]"
            )
            sys.exit(1)

        try:
            await server.setup()
            await transport.run()
        finally:
            await server.teardown()
