using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;

namespace WK5.Core
{
    [Obsolete("Diese Klasse sollte nicht mehr verwendet werden. Sie ist nicht mit .NET 5 und .NET 6 kompatibel.")]
    public class FTPController
    {

        /// <summary>
        /// Die Adresse des Servers zu dem sich verbunden werden soll
        /// </summary>
        public string Server { get; set; }

        /// <summary>
        /// Die Credentials die von dem Methode verwendet werden um sich am Server zu authentifizieren
        /// </summary>
        public NetworkCredential Credentials { get; set; }

        /// <summary>
        /// Erstellt eine neue Instanz des FTPController
        /// </summary>
        /// <param name="server">Der Server zu dem sich mit FTP verbunden werden soll</param>
        /// <param name="credentials">Die Login Credentials die zur Authentifizierung genutzt werden sollen</param>
        public FTPController(string server, NetworkCredential credentials)
        {
            this.Server = server;
            this.Credentials = credentials;
        }

        /// <summary>
        /// Erstellt eine neue Instanz des FTPController
        /// </summary>
        /// <param name="server">Der Server auf dessen FTP Dateien zugegriffen werden soll</param>
        /// <param name="username">Der Benutzername mit dem sich am Remote Server eingeloggt werden soll</param>
        /// <param name="password">Das Passwort mit dem sich am Remote Server eingeloggt werden soll</param>
        public FTPController(string server, string username, string password)
        {
            this.Server = server;
            this.Credentials = new NetworkCredential(username, password);
        }

        /// <summary>
        /// Gibt eine Liste aller Verzeichnisse und Dateien zurück
        /// </summary>
        /// <param name="directory">Das Verzeichnis in dem gesucht werden soll</param>
        /// <returns>Gibt ein Tupel zurück. success beschreibt ob die Anfrage erfolg hatte. data gibt einen <see cref="IEnumerable{T}"/> of string zurück der die Verzeichnisse und Dateien enthält. code gibt den <see cref="FtpStatusCode"/> zurück der im Fehlerfall genauere Informationen enthält</returns>
        public (bool success, IEnumerable<string> data, string message) List(string directory)
        {
            try
            {
                FtpWebRequest request = (FtpWebRequest)WebRequest.Create($"ftp://{this.Server}/{directory}");
                request.Method = WebRequestMethods.Ftp.ListDirectory;
                request.Credentials = this.Credentials;

                FtpWebResponse response = (FtpWebResponse)request.GetResponse();
                using Stream responseStream = response.GetResponseStream();
                using StreamReader reader = new StreamReader(responseStream);

                string values = reader.ReadToEnd();

                reader.Close();
                response.Close();

                if (response.StatusCode == FtpStatusCode.ClosingData)
                {
                    return (true, values.Split("\r\n", StringSplitOptions.RemoveEmptyEntries).AsEnumerable(), "Liste erfolgreich geladen");
                }
                else
                {
                    return (false, new List<string>().AsEnumerable(), $"Fehler beim laden der Datei Liste. Ftp Status Code: {response.StatusCode}");
                }
            }
            catch (Exception ex)
            {
                return (false, new List<string>().AsEnumerable(), ex.Message);
            }
        }

        /// <summary>
        /// Lädt eine Datei von einem FTP Server herunter
        /// </summary>
        /// <param name="file">Der Pfad zu der Datei die heruntergeladen werden soll</param>
        /// <returns>Gibt ein Tupel zurück. success beschreibt ob die Anfrage erfolg hatte. data repräsentiert die Datei in <see cref="byte"/> Form. code gibt den <see cref="FtpStatusCode"/> zurück der im Fehlerfall genauere Informationen enthält</returns>
        public (bool success, byte[] data, string message) Download(string file)
        {
            try
            {
                FtpWebRequest request = (FtpWebRequest)WebRequest.Create($"ftp://{this.Server}/{file}");
                request.Method = WebRequestMethods.Ftp.DownloadFile;
                request.Credentials = this.Credentials;

                FtpWebResponse response = (FtpWebResponse)request.GetResponse();
                using Stream responseStream = response.GetResponseStream();
                using MemoryStream ms = new MemoryStream();
                responseStream.CopyTo(ms);

                if (response.StatusCode == FtpStatusCode.ClosingData)
                {
                    return (true, ms.ToArray(), "Datei erfolgreich heruntergeladen");
                }
                else
                {
                    return (false, new byte[0], $"Fehler beim herunterladen der Datei. Ftp Status Code: {response.StatusCode}");
                }
            }
            catch (Exception ex)
            {
                return (false, new byte[0], ex.Message);
            }
        }

        /// <summary>
        /// Lädt eine Datei vom FTP Server direkt in die angegebene lokale Datei herunter
        /// </summary>
        /// <param name="remoteFile">Der Pfad auf dem Remote Server von dem die Datei heruntergeladen werden soll</param>
        /// <param name="localFile">Der Pfad zu der lokalen Datei in die die Daten geschrieben werden sollen</param>
        /// <param name="force">Gibt an ob die lokale Datei überschrieben werden soll falls sie bereits existiert</param>
        /// <returns>Gibt ein Tupel zurück. success gibt an ob der Download erfolg hatte. message gibt eine detaillierte Nachricht über den Status des Download an</returns>
        public (bool success, string message) Download(string remoteFile, string localFile, bool force = false)
        {
            (bool success, byte[] data, string message) res = this.Download(remoteFile);
            if (res.success)
            {
                try
                {
                    if (File.Exists(remoteFile) && !force)
                    {
                        return (false, "Die Datei existiert bereits");
                    }
                    else
                    {
                        File.WriteAllBytes(localFile, res.data);
                        return (true, "Die Datei wurde erfolgreich heruntergeladen");

                    }
                }
                catch (Exception ex)
                {
                    return (false, ex.Message);
                }
            }
            else
            {
                return (false, res.message);
            }
        }

        /// <summary>
        /// Lädt eine Datei auf einen FTP Server hoch
        /// </summary>
        /// <param name="file">Der Pfad zu der Datei in die auf dem FTP Server geschrieben werden soll</param>
        /// <param name="data">Ein <see cref="byte[]"/> mit den Daten die in die Datei auf dem FTP Server geschrieben werden sollen</param>
        /// <returns>Gibt ein Tupel zurück. success beschreibt ob die Anfrage erfolg hatte. code gibt den <see cref="FtpStatusCode"/> zurück der im Fehlerfall genauere Informationen enthält</returns>
        public (bool success, string message) Upload(string file, byte[] data)
        {
            try
            {
                FtpWebRequest request = (FtpWebRequest)WebRequest.Create($"ftp://{this.Server}/{file}");
                request.Method = WebRequestMethods.Ftp.UploadFile;
                request.Credentials = this.Credentials;

                request.ContentLength = data.Length;
                using Stream requestStream = request.GetRequestStream();
                requestStream.Write(data, 0, data.Length);

                FtpWebResponse response = (FtpWebResponse)request.GetResponse();

                if (response.StatusCode == FtpStatusCode.ClosingData)
                {
                    return (true, "Die Datei wurde erfolgreich hochgeladen");
                }
                else
                {
                    return (false, $"Fehler beim hochladen der Datei. Ftp Status Code: {response.StatusCode}");
                }
            }
            catch (Exception ex)
            {
                return (false, ex.Message);
            }
        }

        /// <summary>
        /// Erstellt ein neues Verzeichnis auf dem FTP Server
        /// </summary>
        /// <param name="directory">Der Pfad zu dem Verzeichnis das erstellt werden soll</param>
        /// <returns>Gibt ein Tupel zurück. success beschreibt ob die Anfrage erfolg hatte. code gibt den <see cref="FtpStatusCode"/> zurück der im Fehlerfall genauere Informationen enthält</returns>
        public (bool success, string message) MakeDirectory(string directory)
        {
            try
            {
                FtpWebRequest request = (FtpWebRequest)WebRequest.Create($"ftp://{this.Server}/{directory}");
                request.Method = WebRequestMethods.Ftp.MakeDirectory;
                request.Credentials = this.Credentials;

                FtpWebResponse response = (FtpWebResponse)request.GetResponse();

                if (response.StatusCode == FtpStatusCode.ClosingData)
                {
                    return (true, "Das Verzeichnis wurde erfolgreich erstellt");
                }
                else
                {
                    return (false, $"Fehler beim erstellen des Verzeichnis. Ftp Status Code: {response.StatusCode}");
                }
            }
            catch (Exception ex)
            {
                return (false, ex.Message);
            }
        }

        /// <summary>
        /// Löscht eine Datei vom FTP Server
        /// </summary>
        /// <param name="file">Der Pfad zu der zu löschenden Datei auf dem FTP Server</param>
        /// <returns>Gibt ein Tupel zurück. success beschreibt ob die Anfrag erfolg hatte. code gibt den <see cref="FtpStatusCode"/> zurück der im Fehlerfall genauere Informationen enthält</returns>
        public (bool success, string message) Delete(string file)
        {
            try
            {
                FtpWebRequest request = (FtpWebRequest)WebRequest.Create($"ftp://{this.Server}/{file}");
                request.Method = WebRequestMethods.Ftp.DeleteFile;
                request.Credentials = this.Credentials;

                FtpWebResponse response = (FtpWebResponse)request.GetResponse();

                if (response.StatusCode == FtpStatusCode.ClosingData)
                {
                    return (true, "Die Datei wurde erfolgreich gelöscht");
                }
                else
                {
                    return (false, $"Fehler beim löschen der Datei. Ftp Status Code: {response.StatusCode}");
                }
            }
            catch (Exception ex)
            {
                return (false, ex.Message);
            }
        }

        /// <summary>
        /// Prüft über die GetDateTimeStamp Funktion ob eine Datei existiert oder nicht
        /// </summary>
        /// <param name="file">Der Pfad zu der Datei auf dem Remote Server deren Existenz geprüft werden soll</param>
        /// <returns>Gibt einen <see cref="bool"/> zurück der angibt ob die Datei existiert oder nicht</returns>
        public bool FileExists(string file)
        {
            try
            {
                FtpWebRequest request = (FtpWebRequest)WebRequest.Create($"ftp://{this.Server}/{file}");
                request.Method = WebRequestMethods.Ftp.GetDateTimestamp;
                request.Credentials = this.Credentials;

                FtpWebResponse response = (FtpWebResponse)request.GetResponse();
                using Stream responseStream = response.GetResponseStream();
                using StreamReader reader = new StreamReader(responseStream);

                string values = reader.ReadToEnd();

                reader.Close();
                response.Close();

                if (response.StatusCode == FtpStatusCode.ActionNotTakenFileUnavailable)
                {
                    return false;
                }
                else
                {
                    return true;
                }
            }
            catch (Exception ex)
            {
                return false;
            }
        }
    }
}
