#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
@author: Fabien Barré for AlgoSecure
# Date: 12/10/2022
# Major version released: 09/2019
# @version: 2.6.0
"""

import time
import os
import sys
import signal
import requests
from pollenisatorgui.core.components.apiclient import APIClient
import pollenisatorgui.core.components.utils as utils
import threading
from getpass import getpass
from pollenisatorgui.core.components.logger_config import logger

event_obj = threading.Event()

class GracefulKiller:
    """
    Signal handler to shut down properly.

    Attributes:
        kill_now: a boolean that can checked to know that it's time to stop.
    """
    kill_now = False

    def __init__(self, app):
        """
        Constructor. Hook the signals SIGINT and SIGTERM to method exitGracefully

        Args:
            app: The appli object to stop
        """
        signal.signal(signal.SIGINT, self.exitGracefully)
        signal.signal(signal.SIGTERM, self.exitGracefully)
        # signal.signal(signal.SIGPIPE, signal.SIG_DFL)
        self.app = app

    def exitGracefully(self, _signum, _frame):
        """
        Set the kill_now class attributes to True. Call the onClosing function of the application given at init.

        Args:
            signum: not used
            frame: not used
        """
        #import traceback
        #traceback.print_stack(_frame)
        self.app.onClosing()
        event_obj.set()
        self.kill_now = True


#######################################
############## MAIN ###################
#######################################

def get_local_git_version():
    return "v2.7.1"

def get_latest_github_version():
    return None
    try:
        # Make a GET request to GitHub's releases endpoint
        url = f"https://api.github.com/repos/fbarre96/PollenisatorGUI/releases/latest"
        response = requests.get(url)
        if response.status_code == 200:
            latest_version = response.json()["tag_name"]
            return latest_version
        else:
            print("Error fetching latest version from GitHub:", response.text)
            return None
    except Exception as e:
        print("Error fetching latest version from GitHub:", e)
        return None

def checkForUpdates():
    latest = get_latest_github_version()
    local = get_local_git_version()
    if local is None or latest is None:
        return False
    if local != latest:
        print(f"New version available: {latest} (current: {local})")
        return True
    return False

def update():
    try:
        # Use pipx to install the latest version of the package
        subprocess.run(['pipx', 'upgrade', 'pollenisator-gui'])
        print("Package updated successfully! Restart the application to use the latest version.")
        sys.exit(0)
    except Exception as e:
        print("Error updating package:", e)

def consoleConnect(force=False, askPentest=True):
    apiclient = APIClient.getInstance()
    abandon = False
    if force:
        apiclient.disconnect()
    while (not apiclient.tryConnection(force=force) or not apiclient.isConnected()) and not abandon:
        success = promptForConnection() is None
    if apiclient.isConnected() and askPentest:
        promptForPentest()
    return not abandon

def promptForConnection():

    clientCfg = utils.loadClientConfig()
    host = clientCfg.get("host", "")
    port = clientCfg.get("port", "5000")
    https = str(clientCfg.get("https", "True")).title() == "True"

    host_result = input(f'Server hostname (defaut {host}):')
    if host_result.strip() == "":
        host_result = host
    port_result = input(f'Server Port (default  {port}):')
    if port_result.strip() == "":
        port_result = port
    https_result = input(f'Use HTTPS (y/N)?').strip() == 'y'

    apiclient = APIClient.getInstance()
    apiclient.reinitConnection()
    res = apiclient.tryConnection({"host":host_result, "port":port_result, "https":https_result})
    if res:
        username_result = input(f'Please input username:')
        password_result = getpass(prompt=f'Please input password:')
        #  pylint: disable=len-as-condition
        loginRes, mustChangePassword = apiclient.login(username_result, password_result)
        if loginRes:
            if mustChangePassword:
                print("You must change your password, connect through a web or graphical client please.")
                return False
            else:
                return loginRes
        else:
            print("Failed to login.")
        return loginRes
    else:
        print("Failed to contact this server.")
    return False

def promptForPentest():
    apiclient = APIClient.getInstance()
    pentests = apiclient.getPentestList()
    if pentests is None:
        pentests = []
    else:
        pentests.sort(key=lambda x: x["creation_date"], reverse=True)
    i = 0
    while i<=0:
        for i_p, pentest in enumerate(pentests):
            print(f"{i_p+1} : {pentest.get('nom', '')}")
        try:
            i = int(input("Selection (1-"+str(len(pentests))+"): "))
            if i <= 0 or i > len(pentests):
                print("Invalid selection, input must be between 1 and "+str(len(pentests)))
                i = 0
        except ValueError:
            i = 0
    apiclient.setCurrentPentest(pentests[i-1]["uuid"])

def parseDefaultTarget(stringToParse):
    if "|" in stringToParse:
        parts = stringToParse.split("|")
        ret = {}
        ret["check_iid"] = parts[0]
        ret["tool_iid"] = parts[1]
        ret["lvl"] = "import"
        return ret

    else:
        return {"check_iid":str(stringToParse), "lvl":"import"}


def pollup():
    """Send a file to pollenisator backend for analysis
    """
    if len(sys.argv) == 2:
        filename = sys.argv[1]
        plugin = "auto-detect"
    elif len(sys.argv) == 3:
        filename = sys.argv[1]
        plugin = sys.argv[2]
    else:
        print("Usage : pollup <filename> [plugin or auto-detect]")
        sys.exit(1)
    uploadFile(filename, plugin)

def uploadFile(filename, plugin='auto-detect'):
    apiclient = APIClient.getInstance()
    apiclient.tryConnection()
    res = apiclient.tryAuth()
    if not res:
        consoleConnect()
    print(f"INFO : Uploading results {filename}")
    msg = apiclient.importExistingResultFile(filename, plugin, parseDefaultTarget(os.environ.get("POLLENISATOR_DEFAULT_TARGET", "")), "")
    print(msg)

def pollwatch():
    from watchdog.observers import Observer
    from watchdog.events import FileSystemEventHandler
    class Handler(FileSystemEventHandler):
        @staticmethod
        def on_any_event(event):
            if event.is_directory:
                return None
            elif event.event_type == 'created':
                # Event is created, you can process it now
                uploadFile(event.src_path)
    apiclient = APIClient.getInstance()
    apiclient.tryConnection()
    res = apiclient.tryAuth()
    if not res:
        consoleConnect()
    observer = Observer()
    observer.schedule(Handler(), '.', recursive=True)
    observer.start()
    try:
        while True:
            time.sleep(5)
    finally:
        observer.stop()
        observer.join()



def main():
    """Main function. Start pollenisator application
    """

    print("""
.__    ..              ,
[__) _ || _ ._ * __ _.-+- _ ._.
|   (_)||(/,[ )|_) (_] | (_)[

""")

    import tkinter as tk
    import customtkinter
    import pollenisatorgui.core.components.utilsUI as utilsUI
    from pollenisatorgui.core.application.appli import Appli

    customtkinter.set_default_color_theme(utilsUI.getColorTheme())

    event_obj.clear()
    gc = None
    update_available = checkForUpdates()
    if update_available:
        ask_update = input("An update is available, do you want to update now (y/N)?").strip().lower() == "y"
        if ask_update:
            update()
    app = Appli()
    try:
        gc = GracefulKiller(app)
        if not app.quitting:
            app.mainloop()
        print("Exiting tkinter main loop")
    except tk.TclError:
        pass
    try:
        try:
            app.destroy()
            print("Destroying app window")
        except tk.TclError:
            pass
        if gc is not None:
            gc.kill_now = True
        event_obj.set()
        app.onClosing()
    except Exception as e:
        print("Error while closing app")
        print(e)
if __name__ == '__main__':
    main()
