# -*- coding: utf-8 -*-

__author__ = '(C) 2023 by Gerald Kogler'
__date__ = '1/10/2023'
__copyright__ = 'Copyright 2023, 300.000 Km/s'
__license__ = 'GPLv3 license'

import os
import ftplib

try:
    import paramiko
except:
    print("ERROR! Can't load library 'paramiko', trying to install it from pip")

    import subprocess
    try:
        subprocess.run(['python', '-m', 'pip', 'install', 'paramiko'])
        import paramiko
    except:
        try:
            subprocess.run(['python3', '-m', 'pip', 'install', 'paramiko'])
            import paramiko
        except:
            print("Failed to import 'paramiko', so you can't use SFTP upload.")

from qgis.PyQt import uic
from qgis.PyQt.QtGui import QIcon
from qgis.PyQt import QtWidgets
from qgis.PyQt.QtWidgets import QPushButton, QDialogButtonBox, QListWidgetItem

from qgis.core import QgsProject, Qgis
from .utils import Utils

FORM_CLASS, _ = uic.loadUiType(os.path.join(os.path.dirname(__file__), 'qwv_dialog3_base.ui'))


class QWVDialog3(QtWidgets.QDialog, FORM_CLASS):

    def __init__(self, iface, parent=None, _deactivateSFTP=None):
        """Constructor."""
        super(QWVDialog3, self).__init__(parent)
        # Set up the user interface from Designer through FORM_CLASS.
        # After self.setupUi() you can access any designer object by doing
        # self.<objectname>, and you can use autoconnect slots - see
        # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
        # #widgets-and-dialogs-with-auto-connect

        self.iface = iface
        self.setupUi(self)
        self._deactivateSFTP = _deactivateSFTP

        # protocol
        self.dlg3Publish_protocol.addItem(Utils.tr('FTP - File Transfer Protocol (port 21)'))

        if not self._deactivateSFTP:
            self.dlg3Publish_protocol.addItem(Utils.tr('SFTP - SSH File Transfer Protocol (port 22)'))

        # add save settings button
        self.runBtn = QPushButton(Utils.getIcon("system-run"), Utils.tr("Run"))
        self.button_box.addButton(self.runBtn, QDialogButtonBox.ApplyRole)

        self.saveBtn = QPushButton(Utils.getIcon("document-save"), Utils.tr("Save Settings"))
        self.button_box.addButton(self.saveBtn, QDialogButtonBox.ActionRole)

        self.closeBtn = QPushButton(Utils.getIcon("window-close"), Utils.tr("Close"))
        self.button_box.addButton(self.closeBtn, QDialogButtonBox.ActionRole)

        self.helpBtn = QPushButton(Utils.getIcon("help"), Utils.tr("Help"))
        self.button_box.addButton(self.helpBtn, QDialogButtonBox.HelpRole)

        self.dlg3Publish_test.clicked.connect(self.connectToServer)


    def readUIFields(self):
        """Read UI field values."""

        self.local_folder = self.dlg3Publish_project.filePath()
        self.protocol = self.dlg3Publish_protocol.currentIndex()
        self.hostname = self.dlg3Publish_host.text()
        self.remote_folder = self.dlg3Publish_path.text()
        self.username = self.dlg3Publish_user.text()
        self.password = self.dlg3Publish_password.text()


    def connectToServer(self, publish=False):
        """Publish content using FTP or SFTP."""

        self.readUIFields()

        if publish and (not self.local_folder or not self.remote_folder):
            self.messageBar.pushMessage(Utils.tr("Warning"), Utils.tr("You have to define local and remote folder in Project Settings in order to use FTP"), level=Qgis.Warning, duration=3)
            return False

        if (self.inputsFtpOk(self.hostname, self.username, self.password)):
            if self.protocol == 0:
                if publish:
                    return self.publishFTP(self.local_folder, self.remote_folder)
                else:
                    return self.publishFTP()
            elif self.protocol == 1:
                if publish:
                    return self.publishSFTP(self.local_folder, self.remote_folder)
                else:
                    return self.publishSFTP()

        else:
            return False


    def inputsFtpOk(self, host=None, user=None, password=None):
        """Data for FTP connection available."""

        if host == "" or user == "" or password == "":
            self.messageBar.pushMessage(Utils.tr("Warning"), Utils.tr("You have to define Host, User and Password in Project Settings in order to use SFTP"), level=Qgis.Warning, duration=3)
            return False
        
        return True


    def publishSFTP(self, local_folder=False, remote_folder=False):
        """Publish content using SFTP."""

        ssh_client = paramiko.SSHClient()
        ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        try:
            ssh_client.connect(
                hostname = self.hostname,
                username = self.username,
                password = self.password
            )
        except Exception:
            self.messageBar.pushMessage(Utils.tr("Warning"), Utils.tr("Could not connect to SFTP server {}").format(self.hostname), level=Qgis.Warning, duration=3)
            return False

        ftp_client = ssh_client.open_sftp()

        if local_folder and remote_folder:
            # check if folder exists
            try:
                ftp_client.chdir(remote_folder)
            except IOError:
                # path doesn't exist -> create it
                ftp_client.mkdir(remote_folder)

            # show progress bar
            fileCount = 0
            for root_dir, cur_dir, files in os.walk(local_folder):
                fileCount += len(files)
            self.progress = Utils.initProgressBar(fileCount, "Uploading files...", self.messageBar)

            # Uploading content from local to remote machine
            self.sftp_cwd_and_create(ftp_client, local_folder, os.path.sep + remote_folder)

            # remove progress bar
            self.messageBar.clearWidgets()
            
            #self.messageBar.pushMessage(Utils.tr("Success"), Utils.tr("Folder uploaded successfully"), level=Qgis.Success, duration=3)
        else:
            # only test connection
            self.messageBar.pushMessage(Utils.tr("Success"), Utils.tr("SFTP connection to {} established successfully").format(self.hostname), level=Qgis.Success, duration=3)

        ftp_client.close()

        return True


    def sftp_cwd_and_create(self, ftp_client, local_folder, remote_folder):
        files = os.listdir(local_folder)
        os.chdir(local_folder)
        for f in files:
            current_path = os.path.join(local_folder, f)
            remote_path = remote_folder + os.path.sep + f

            if os.path.isfile(current_path):
                ftp_client.put(current_path, remote_path)

                # update progress bar
                self.progress.setValue(self.progress.value() + 1)
            else:
                try:
                    ftp_client.mkdir(remote_path)
                except IOError:
                    ftp_client.chdir(remote_path)

                self.sftp_cwd_and_create(ftp_client, current_path, remote_path)


    def publishFTP(self, local_folder=False, remote_folder=False):
        """Publish content using FTP."""

        self.ftp = ftplib.FTP()
        try:
            self.ftp.connect(self.hostname, 21, timeout=10)
        except Exception:
            self.messageBar.pushMessage(Utils.tr("Warning"), Utils.tr("Could not connect to FTP server {}").format(self.hostname), level=Qgis.Warning, duration=3)
            return False

        try:
            self.ftp.login(self.username, self.password)
        except Exception:
            self.messageBar.pushMessage(Utils.tr("Warning"), Utils.tr("Login failed for user {}").format(self.username), level=Qgis.Warning, duration=3)
            return False

        if local_folder and remote_folder:
            # Uploading file from local to remote machine
            self.ftp_cwd_and_create(self.remote_folder)

            # show progress bar
            fileCount = 0
            for root_dir, cur_dir, files in os.walk(local_folder):
                fileCount += len(files)
            self.progress = Utils.initProgressBar(fileCount, "Uploading files...", self.messageBar)

            if self.ftp_uploadPath(self.local_folder):

                self.messageBar.clearWidgets()

                #self.messageBar.pushMessage(Utils.tr("Success"), Utils.tr("Folder uploaded successfully"), level=Qgis.Success, duration=3)
        else:
            # only test connection
            self.messageBar.pushMessage(Utils.tr("Success"), Utils.tr("FTP connection to {} established successfully").format(self.hostname), level=Qgis.Success, duration=3)
        
        self.ftp.close()


    def ftp_cwd_and_create(self, folder):
        """recursively changes directory and creating new folders as required."""

        if not folder:
            return
        try:
            self.ftp.cwd(folder)
            #print('Folder {} already exists'.format(folder))
        except Exception:
            parent, base = os.path.split(folder)
            self.ftp_cwd_and_create(parent)
            if base:
                #print('Creating folder {}'.format(base))
                self.ftp.mkd(base)
                self.ftp.cwd(base)
                #print('Folder {} created successfully'.format(base))


    def ftp_uploadPath(self, path):
        """recursively uploads content of folder"""

        files = os.listdir(path)
        os.chdir(path)
        for f in files:
            current_path = os.path.join(path, f)
            if os.path.isfile(current_path):
                fh = open(f, 'rb')
                self.ftp.storbinary('STOR %s' % f, fh)
                fh.close()

                # update progress bar
                self.progress.setValue(self.progress.value() + 1)

            elif os.path.isdir(current_path):
                #print('Creating folder {}'.format(f))
                try:
                    self.ftp.mkd(f)
                except Exception:
                    pass
                self.ftp.cwd(f)
                if not self.ftp_uploadPath(current_path):
                    return False

        #self.messageBar.pushMessage(Utils.tr("Success"), Utils.tr("Folder uploaded successfully"), level=Qgis.Success, duration=3)

        self.ftp.cwd('..')
        os.chdir('..')

        return True