import os
import toml
import json

import rich_click as click

from questionary import text, confirm

from novara.utils import logger
from novara.request import request, JSONDecodeError
from novara.guess import guess_pip_packages


def generate_dockerfile():
    try:
        with open("novara.toml", "r") as f:
            toml_raw = f.read()
    except (OSError, FileNotFoundError):
        raise click.ClickException("novara.toml either not there or unaccessable")

    logger.info("read toml file")

    logger.info("generating Dockerfile")
    r = request.post(
        f"api/build/dockerfile/toml/", data=json.dumps(toml_raw)
    )
    if not r.ok:
        raise click.ClickException(
            f"failed generating dockerfile with message: {r.text}"
        )
        exit()

    try:
        dockerfile_str = r.json()
    except JSONDecodeError:
        raise click.ClickException(
            f"failed to decode response as json: {r.text[:20] if len(r.text) > 20 else r.text}"
        )

    try:
        with open("Dockerfile", "w") as docker_file:
            docker_file.write(dockerfile_str)
    except OSError:
        raise click.ClickException("Dockerfile is unaccessable")
        exit()

    logger.info("generated Dockerfile")
    logger.info("run 'novara run local|remote' to start a container or remote container")


@click.command()
@click.option("--pip", default=None, help="add a new pip packages")
@click.option("--apt", default=None, help="add a new apt packages")
@click.option("--apk", default=None, help="add a new apk packages")
@click.option("--snap", default=None, help="add a new snap packages")
@click.option("--dnf", default=None, help="add a new dnf packages")
@click.option("--no-guess", default=False, help='disable guessing the necessary python packages', is_flag=True)
@click.option("--directory", "-d", default="./", help="path to exploit")
def generate(pip, apt, apk, snap, dnf, no_guess, directory):
    """(re)generate the Dockerfile and add dependencies if necessary"""

    directory = os.path.join(directory, "novara.toml")

    if any([pip, apt, apk, snap, dnf]):
        logger.info("adding packages to novara.toml file")

    try:
        with open(directory, "r") as f:
            exploit_config:dict[str, dict[str, dict[str, list[str]]]] = toml.load(f)
    except (OSError, FileNotFoundError):
        raise click.ClickException(f"{directory} either not there or unaccessable")

    docker_config = exploit_config.get("docker", None)
    if docker_config is None:
        raise click.ClickException("docker key not found in toml")
        
    dependencies = docker_config.get("dependencies", None)
    if dependencies is None:
        raise click.ClickException("docker.dependencies key not found in toml")

    pip_packages = dependencies.get("pip", None)
    apt_packages = dependencies.get("apt", None)
    apk_packages = dependencies.get("apk", None)
    dnf_packages = dependencies.get("dnf", None)
    snap_packages = dependencies.get("snap", None)

    guessed_packages = []
    if not no_guess:
        guessed_packages = guess_pip_packages(exploit_config.get('script_file_name', 'exploit.py'), exclude=pip_packages or [])

        if guessed_packages:
            logger.info(f'Found packages: {", ".join(guessed_packages)}')

            if confirm(f'Do you want to add these packages ({", ".join(guessed_packages)})?', default=True).ask():
                for pkg in guessed_packages:
                    package = text('Is the name/version of this package correct (to omit just delete name)?', default=pkg).ask()
                    if not package:
                        continue
                    if not pip_packages:
                        pip_packages = []
                    pip_packages.append(package)

    if pip is not None:
        dependencies.update(
            {"pip": [pip] if pip_packages is None else pip_packages + [pip] }
        )
    
    if apt is not None:
        dependencies.update(
            {"apt": [apt] if apt_packages is None else apt_packages + [apt]}
        )
    
    if apk is not None:
        dependencies.update(
            {"apk": [apk] if apk_packages is None else apk_packages + [apk]}
        )
    
    if dnf is not None:
        dependencies.update(
            {"dnf": [dnf] if dnf_packages is None else dnf_packages + [dnf]}
        )
    
    if snap is not None:
        dependencies.update(
            {"snap": [snap] if snap_packages is None else snap_packages + [snap]}
        )
    
    try:
        with open(directory, "w") as f:
            f.write(
                "# novara configuration file autogenerated by the novara cli, only edit if you know what you are doing\n\n"
            )
            toml.dump(exploit_config, f)
    
    except (OSError, FileNotFoundError):
        raise click.ClickException(f"{directory} either not there or unaccessable")
    
    logger.info("updated novara.toml file")

    generate_dockerfile()
