"""Help the user to create a new pentest database.
"""
import tkinter as tk
import tkinter.ttk as ttk
from customtkinter import *
from pollenisatorgui.core.application.dialogs.ChildDialogAskText import ChildDialogAskText
from pollenisatorgui.core.application.dialogs.ChildDialogQuestion import ChildDialogQuestion
from pollenisatorgui.core.application.dialogs.ChildDialogNewPentest import ChildDialogNewPentest
from pollenisatorgui.core.forms.formpanel import FormPanel
from pollenisatorgui.core.components.apiclient import APIClient
from pollenisatorgui.core.application.scrollableframexplateform import ScrollableFrameXPlateform
import pollenisatorgui.core.components.utils as utils
from datetime import datetime
from PIL import Image, ImageTk
from pollenisatorgui.core.application.paginable import Paginable

class ChildDialogPentests(CTkToplevel):
    """
    Open a child dialog of a tkinter application to ask details about
    a new pentest database to create.
    """

    def __init__(self, parent, pentests):
        """
        Open a child dialog of a tkinter application to ask details about
        the new pentest.

        Args:
            parent: the tkinter parent view to use for this window construction.
        """
        super().__init__(parent)
        self.attributes("-type", "dialog")
        self.parent = parent
        self.pentests = pentests
        self.title("Pentest manager")
        self.resizable(True, True)
        monitor = utils.get_screen_where_widget(parent)
        self.geometry(f"{920}x{600}+{monitor.x+(monitor.width//2)-(920//2)}+{monitor.y+(monitor.height//2)-(600//2)}")
        
        self.rvalue = None
        
        self.bind("<Escape>", self.quit)
        self.link_image = CTkImage(Image.open(utils.getIcon("link.png"))) # button with text
        self.delete_image = tk.PhotoImage(file=utils.getIcon("delete.png")) # icon button
        # Load the image
        image=Image.open(utils.getIcon("copy_small.png"))
        self.copy_image = ImageTk.PhotoImage(image)
        image=Image.open(utils.getIcon("save_small.png"))
        self.export_image = ImageTk.PhotoImage(image)

        self.mainFrame = ScrollableFrameXPlateform(self)
        self.reloadUI()
        self.mainFrame.pack(fill=tk.BOTH, ipadx=10, ipady=10, expand=1)
        try:
            self.wait_visibility()
            self.transient(self.parent)
            self.focus_force()
            self.grab_set()
            self.lift()

        except tk.TclError:
            pass

    def reloadUI(self, reload_pentests=False):
        for widget in self.mainFrame.winfo_children():
            widget.destroy()
        if self.pentests is None or reload_pentests:
            apiclient = APIClient.getInstance()
            self.pentests = apiclient.getPentestList()
        else:
            self.pentests = self.pentests
        self.pentests.sort(key=lambda x: x["creation_date"], reverse=True)
        self.form = FormPanel()
        form1 = self.form.addFormPanel(fill=tk.BOTH)
        form1.addFormLabel("My Pentests")
        self.plus_image = CTkImage(Image.open(utils.getIcon("plus.png")))
        self.import_image = CTkImage(Image.open(utils.getIcon("import.png")))
        form1.addFormButton("Create new Pentest", self.selectNewPentest, image=self.plus_image, side=tk.LEFT)
        form1.addFormButton("Import archive", self.importPentest,  side=tk.LEFT, image=self.import_image)
        form1.addFormButton("Disconnect", self.disconnect,  side=tk.LEFT, padx=10)
        self.form.addFormSeparator()
        self.searchBar = self.form.addFormStr("Search", placeholder="Filter", binds={"<KeyRelease>": self.search}, side=tk.TOP, padx=10)
        self.form2 = self.form.addFormPanel(grid=True, fill=tk.BOTH, make_uniform_column=4)
        self.form2.addFormLabel("Pentest name", text="Pentest name", bold=True, row=0, column=0, sticky=tk.W)
        self.form2.addFormLabel("Creator", text="Creator", bold=True, row=0, column=1, sticky=tk.W)
        self.form2.addFormLabel("Date", text="Date", bold=True, row=0, column=2, sticky=tk.W)
        self.form2.addFormLabel("Actions", text="Actions", bold=True, row=0, column=3, sticky=tk.W)
        self.form2.addFormSeparator(row=1, column=0, columnspan=4, sticky=tk.EW)
        self.paginated_pentests = Paginable(self.mainFrame, self.callback_insert, self.callback_reset, self.callback_get_value, self.redraw)
        for pentest in self.pentests:
            pentest["iid"] = pentest["uuid"]
            self.paginated_pentests.addPaginatedInfo(pentest, False)
        content_view = self.paginated_pentests.getContentView()
        self.form.constructView(content_view)
        self.paginated_pentests.pack(fill=tk.BOTH, ipadx=10, ipady=10, expand=1)
        self.mainFrame.pack(fill=tk.BOTH, ipadx=10, ipady=10, expand=1)
        self.paginated_pentests.setPaginationPanel()
        try:
            self.wait_visibility()
            self.transient(self.parent)
            self.focus_force()
            self.grab_set()
            self.lift()
        except tk.TclError:
            pass

    def redraw(self):
        self.form2.redraw()

    def callback_insert(self, items):
        row = len(self.paginated_pentests.getShownInfos())*2 
        for pentest in items:
            date = pentest["creation_date"]
            date_o = datetime.fromisoformat(date)
            self.form2.addFormLabel("pentest_name", text=pentest["nom"],row=row, column=0, sticky=tk.W)
            self.form2.addFormLabel("pentest_creator", text=pentest["owner"], row=row, column=1, sticky=tk.W)
            self.form2.addFormLabel("pentest_date", text=utils.dateToString(date_o), row=row, column=2, sticky=tk.W)
            formAction = self.form2.addFormPanel(row=row, column=3, sticky=tk.W)
            formAction.addFormButton("", self.exportPentest, style="icon.TButton", side=tk.RIGHT, image=self.export_image, tooltip="Export", infos={"name":pentest["nom"], "uuid":pentest["uuid"]})
            formAction.addFormButton("", self.wrapCopyDb, style="icon.TButton", side=tk.RIGHT, image=self.copy_image, tooltip="duplicate pentest", infos={"name":pentest["nom"], "uuid":pentest["uuid"]})
            formAction.addFormButton("", self.deleteAPentest, style="icon.TButton", side=tk.RIGHT, image=self.delete_image, tooltip="delete pentest" , infos={"name":pentest["nom"], "uuid":pentest["uuid"]})
            formAction.addFormButton("Open", self.openPentest, width=55,image=self.link_image, side=tk.RIGHT, infos={"name":pentest["nom"], "uuid":pentest["uuid"]})
            self.form2.addFormSeparator(row=row+1, column=0, columnspan=4, sticky=tk.EW)
            row+=2
        

    def callback_reset(self):
        self.form2.clear()
        self.form2.addFormLabel("Pentest name", text="Pentest name", bold=True, row=0, column=0, sticky=tk.W)
        self.form2.addFormLabel("Creator", text="Creator", bold=True, row=0, column=1, sticky=tk.W)
        self.form2.addFormLabel("Date", text="Date", bold=True, row=0, column=2, sticky=tk.W)
        self.form2.addFormLabel("Actions", text="Actions", bold=True, row=0, column=3, sticky=tk.W)
        self.form2.addFormSeparator(row=1, column=0, columnspan=4, sticky=tk.EW)

    def callback_get_value(self, item, arg_n):
        columns = ["nom", "owner", "creation_date"]
        return item[columns[arg_n]]

    def search(self, event=None):
        """
        Search in treeviews
        """
        if event is not None:
            if event.keysym != "BackSpace" and event.keysym != "Delete" and (len(event.keysym) > 1 or not event.keysym.isalnum()):
                return
        search = self.searchBar.getValue()
        self.paginated_pentests.filter(search, search, search, check_all=False, check_case=False)


    def disconnect(self, event=None):
        apiclient = APIClient.getInstance()
        apiclient.disconnect()
        self.quit()

    def openPentest(self, event, infos):
        name = infos.get("uuid")
        self.rvalue = name
        self.quit()
    
    def deleteAPentest(self, event, infos):
        """
        Ask a user a pentest name then delete it.
        """
        apiclient = APIClient.getInstance()
        pentest = infos.get("uuid")
        pentestName = infos.get("name")
        dialog = ChildDialogQuestion(self, "Pentest deletion confirmation", "You are going to delete permanently the database \""+pentestName+"\". Are you sure ?")
        self.wait_window(dialog.app)
        if dialog.rvalue == "Yes":
            res = apiclient.doDeletePentest(pentest)
            if res:
                try:
                    path = os.path.join(utils.getLocalDir(), "states/", "."+pentestName)
                    os.remove(path)
                except FileNotFoundError:
                    pass
            self.reloadUI(reload_pentests=True)
        


    def selectNewPentest(self, _event=None):
        """
        Ask a user for a new pentest name. Then creates it.
        """
        validPentest = False
        default = {}
        while not validPentest:
            dialog = ChildDialogNewPentest(None, default)
            dialog.wait_window()
            if dialog.rvalue is None:
                return
            if isinstance(dialog.rvalue, dict):
                default = dialog.rvalue
                pentest_name = dialog.rvalue["name"]
                mission_name = dialog.rvalue["mission_name"]
                client_name = dialog.rvalue["client_name"]
                lang = dialog.rvalue["lang"]
                pentest_type = dialog.rvalue["type"]
                start_date = dialog.rvalue["start"]
                end_date = dialog.rvalue["end"]
                scope = dialog.rvalue["scope"]
                settings = dialog.rvalue["settings"]
                settings["mission_name"] = mission_name
                settings["client_name"] = client_name
                settings["lang"] = lang
                pentesters = dialog.rvalue["pentesters"]
                validPentest, msg = self.newPentest(pentest_name, pentest_type, start_date, end_date, scope, settings, pentesters)
                if validPentest:
                    self.rvalue = msg
                    self.quit()

    def newPentest(self, pentestName, pentest_type, start_date, end_date, scope, settings, pentesters):
        """
        Register the given pentest name into database and opens it.

        Args:
            pentestName: The pentest database name to register in database.
        """
        succeed = False
        if pentestName is not None:
            apiclient = APIClient.getInstance()
            succeed, msg = apiclient.registerPentest(pentestName, pentest_type, start_date, end_date, scope, settings, pentesters)
            if not succeed:
                tk.messagebox.showinfo("Forbidden", msg)
        return succeed, msg
            

    def quit(self, _event=None):
        self.destroy()

    def wrapCopyDb(self, _event, infos):
        """
        Call default copy database from a callback event.

        Args:
            _event: not used but mandatory
        """
        apiclient = APIClient.getInstance()
        pentestName = infos.get("uuid")
        success = apiclient.setCurrentPentest(pentestName)
        if not success:
            tk.messagebox.showerror("Failed", "Failed to connect to the selected pentest")
            return
        dialog = ChildDialogAskText(self, "Enter copy's name  of "+apiclient.getCurrentPentest()+" database name", multiline=False)
        self.wait_window(dialog.app)
        name = dialog.rvalue
        if name is None:
            return
        apiclient.copyDb(apiclient.getCurrentPentest(), name)
        self.reloadUI(reload_pentests=True)

    def importPentest(self, event=None):
        """
        Import a pentest archive file gunzip to database.
        Args:
            name: The filename of the gunzip database exported previously
        """
        apiclient = APIClient.getInstance()
        filename = ""
        f = tk.filedialog.askopenfilename(parent=self, defaultextension=".gz")
        if not isinstance(f, str):  # asksaveasfile return `None` if dialog closed with "cancel".
            return
        if len(str(f).strip()) == 0:
            return
        filename = str(f)
        original_dbname = ChildDialogAskText(self, "Enter the original database name", multiline=False)
        self.wait_window(original_dbname.app)
        if original_dbname.rvalue is None:
            return
        
        success = apiclient.importDb(filename, original_dbname.rvalue)
        if success:
            tk.messagebox.showinfo("Database import ", "Database import suceeded")
        else:
            tk.messagebox.showerror("Database import ", "Database import failed")
        self.reloadUI(reload_pentests=True)

    def exportPentest(self, event, info):
        """
        Dump a pentest database to an archive file gunzip.
        """
        apiclient = APIClient.getInstance()
        pentestName = info.get("uuid")
        success = apiclient.setCurrentPentest(pentestName)
        if not success:
            tk.messagebox.showerror("Failed", "Failed to connect to the selected pentest")
            return
        success, msg = apiclient.dumpDb(self, pentestName)
        if success is None:
            # closed
            return
        if not success:
            tk.messagebox.showerror("Database export error", msg)
        else:
            tk.messagebox.showinfo("Database export completed", msg)