import os
from typing import List

import typer
from pymisp import PyMISP
from typer import Typer, Option, Argument

from vt2m.lib import lib
from vt2m.lib.output import print_err
from vt2m.subcommands import notifications, retrohunts

app = Typer()
state = {
    "verbose": False,
    "quiet": False
}


@app.command()
def query(
        query: str = Argument(..., help="VirusTotal Query"),
        uuid: str = Option(..., "--uuid", "-u", help="MISP event UUID"),
        url: str = Option(None, "--url", "-U", help="MISP URL - can be passed via MISP_URL env"),
        key: str = Option(None, "--key", "-K", help="MISP API Key - can be passed via MISP_KEY env"),
        vt_key: str = Option(None, "--vt-key", "-k", help="VirusTotal API Key - can be passed via VT_KEY env"),
        comment: str = Option("", "--comment", "-c", help="Comment for new MISP objects."),
        limit: int = Option(100, "--limit", "-l", help="Limit of VirusTotal objects to receive"),
        limit_relations: int = Option(
            40,
            "--limit-relations",
            "-L",
            help="Limit the amount of related objects. Note that this is for every relation queries."),
        relations: str = Option(
            "",
            "--relations",
            "-r",
            help=f"Relations to resolve via VirusTotal, available relations are: {', '.join(lib.all_relations)}"
        ),
        detections: int = Option(0, "--detections", "-d",
                                 help="Amount of detections a related VirusTotal object must at least have"),
        extract_domains: bool = Option(False, "--extract-domains", "-D",
                                       help="Extract domains from URL objects and add them as related object."),
        filter: List[str] = Option(
            [],
            "--filter",
            "-f",
            help="Filtering related objects by matching this string(s) against json dumps of the objects."
        ),
        pivot: str = Option(
            None,
            "-p",
            "--pivot",
            help="Pivot from the given query before resolving relationships. This must be a valid VT file relation "
                 f"({', '.join(lib.file_relations)})."
        ),
        pivot_limit: int = Option(40, "-P", "--pivot-limit", help="Limit the amount of files returned by a pivot."),
        pivot_comment: str = Option(None, "-C", "--pivot-comment", help="Comment to add to the initial pivot object."),
        pivot_relationship: str = Option(
            "related-to",
            "--pivot-relationship",
            help="MISP relationship type for the relation between the initial pivot object and the results."
        )
):
    """
    Query VT for files and add them to a MISP event
    """
    if not url:
        url = os.getenv("MISP_URL", None)

    if not key:
        key = os.getenv("MISP_KEY", None)

    if not vt_key:
        vt_key = os.getenv("VT_KEY", None)

    if not url or not key or not vt_key:
        print_err("[ERR] URL and key must be given either through param or env.")

    if pivot and pivot not in lib.file_relations:
        print_err("[ERR] Pivot relationship is not valid or not implemented.")

    misp = PyMISP(url, key)
    misp.global_pythonify = True
    event = misp.get_event(uuid)
    results = lib.vt_query(
        api_key=vt_key,
        query=query,
        limit=limit
    )

    pivot_object = None
    if pivot:
        pivot_results = []
        for r in results:
            pivot_results.extend(
                lib.pivot_from_hash(
                    api_key=vt_key,
                    sha256_hash=r["attributes"]["sha256"],
                    rel=pivot,
                    limit=pivot_limit,
                    disable_output=state["quiet"]
                )
            )
        if len(pivot_results) == 0:
            print_err("[PIV] No files returned.")
            raise typer.Exit(-1)
        else:
            pivot_object = lib.process_results(
                results=results,
                event=event,
                comment=pivot_comment,
                disable_output=state["quiet"],
                extract_domains=False
            )[0]
            results = pivot_results

    created_objects = lib.process_results(
        results=results,
        event=event,
        comment=comment,
        disable_output=state["quiet"],
        extract_domains=extract_domains
    )
    lib.process_relations(
        api_key=vt_key,
        objects=created_objects,
        event=event,
        relations_string=relations,
        detections=detections,
        disable_output=state["quiet"],
        extract_domains=extract_domains,
        limit=limit_relations,
        filter=filter
    )
    if pivot and pivot_object:
        for obj in created_objects:
            lib.add_reference(pivot_object, obj.uuid, pivot_relationship)
    event.published = False
    misp.update_event(event)


@app.callback()
def callback(quiet: bool = Option(False, is_flag=True, help="No output except stderr")):
    state["quiet"] = quiet


app.add_typer(notifications.app, name="notifications")
app.add_typer(notifications.app, name="no")
app.add_typer(retrohunts.app, name="retrohunts")
app.add_typer(retrohunts.app, name="re")

if __name__ == "__main__":
    app()
