using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace KarleyLibrary.Erweiterungen
{
    /// <summary>
    /// Fügt sinnvolle Datei operationen hinzu
    /// </summary>
    public class FileErweiterung
    {
        /// <summary>
        /// Prüft ob eine Datei im Dateisystem blockiert ist oder nicht
        /// </summary>
        /// <param name="file">Der Pfad der zu prüfenden Datei</param>
        /// <returns>Gibt einen <see cref="bool"/> zurück der angibt ob sich die Datei lesen lässt</returns>
        public static bool IsLocked(string file)
        {
            try
            {
                FileInfo fileInfo = new FileInfo(file);
                using FileStream stream = fileInfo.Open(FileMode.Open, FileAccess.Read, FileShare.None);
                stream.Close();
            }
            catch (Exception)
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// Liest die erste Zeile einer Datei
        /// </summary>
        /// <param name="file">Die zu lesende Datei</param>
        /// <param name="tries">Die Versuche die unternommen werden sollen um die Datei zu lesen</param>
        /// <param name="timeout">Der Timeout zwischen Versuchen</param>
        /// <returns>Gibt ein Tupel zurück, wo success für den Erfolg der Method, line die erste Zeile der Datei und message die Nachricht im Fehlerfall darstellt</returns>
        public static (bool success, string? line, string message) ReadFirstLine(string file, int tries = 20, int timeout = 100)
        {
            try
            {


                int tryCount = 1;
                while (IsLocked(file))
                {
                    if (tryCount > tries)
                    {
                        return (false, "", "Die Anzahl der Versuche zum lesen der Datei wurden überschritten");
                    }
                    else
                    {
                        tryCount++;
                    }
                    Thread.Sleep(timeout);

                }

                using StreamReader reader = new StreamReader(file);
                string? line = reader.ReadLine();
                reader.Close();

                return (true, line, "");
            }
            catch (Exception ex)
            {
                return (false, "", ex.Message);
            }
        }

        /// <summary>
        /// Sucht einen passenden Namen für eine Datei und beachtet dabei Namens Duplikate
        /// </summary>
        /// <param name="file">Die Datei für die eine passender Name gesucht werden soll</param>
        /// <returns>Gibt ein Tupel zurück. success gibt an ob die Methode erfolg hatte, file gibt den neuen Dateinamen an und message ist die nachricht die im Fehlerfall zurück gegeben wird</returns>
        public static (bool success, string file, string message) FindSuitableFileName(string file)
        {
            try
            {
                string ext = Path.GetExtension(file);
                for (int i = 1; i < Int32.MaxValue; i++)
                {
                    string newFile = file.Replace(ext, $"_{i}{ext}");
                    if (!File.Exists(newFile))
                    {
                        return (true, newFile, "Passenden Dateinamen gefunden");
                    }
                }

                return (false, "", "Es konnte kein passender Dateiname gefunden werden");
            }
            catch (Exception ex)
            {
                return (false, "", ex.Message);
            }
        }

        /// <summary>
        /// Verschiebt Rekursiv alle Dateien aus Ordnern und SubOrdnern. Verschiebt nur Dateien und nicht die Ordnerstruktur
        /// </summary>
        /// <param name="from">Die Quelle des verschieben</param>
        /// <param name="to">Der Zielpfad in den die Dateien verschoben werden sollen</param>
        /// <returns>Gibt ein Tupel zurück. success gibt an ob das verschieben Erfolg hatte und message gibt die Nachricht im Fehlerfall an</returns>
        public static (bool success, string message) MoveRecursive(string from, string to)
        {
            try
            {
                foreach (string file in Directory.GetFiles(from))
                {
                    FileInfo info = new FileInfo(file);
                    var exportResult = FindSuitableFileName(info.FullName);

                    if (exportResult.success)
                    {
                        File.Move(info.FullName, Path.Combine(to, Path.GetFileName(exportResult.file)));
                    }
                    else
                    {
                        return (false, "Es konnte kein passender Dateiname zum verschieben gefunden werden");
                    }
                }

                foreach (string dir in Directory.GetDirectories(from))
                {
                    DirectoryInfo info = new DirectoryInfo(Path.Combine(from, dir));
                    return MoveRecursive(info.FullName, to);
                }

                return (true, "Dateien erfolgreich verschoben");
            }
            catch (Exception ex)
            {
                return (false, ex.Message);
            }
        }

        /// <summary>
        /// Erstellt ein Temporäres Verzeichnis mit einem zufällig generierten namen
        /// </summary>
        /// <returns>Gibt ein Tupel zurück. success gibt an ob die Methode Erfolg hatte, dir gibt das Temporäre Verzeichnis an, message gibt die Nachricht im Fehlerfall an</returns>
        public static (bool success, string dir, string message) CreateTempDir()
        {
            try
            {
                string tmpDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
                while (Directory.Exists(tmpDir))
                {
                    tmpDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
                }

                Directory.CreateDirectory(tmpDir);
                return (true, tmpDir, "Temporäres Verzeichnis erfolgreich erstellt");
            }
            catch (Exception ex)
            {
                return (false, "", ex.Message);
            }
        }

        public static (bool success, string nachricht) EnsureCreateDirectory(string dir)
        {
            string[] dirs = dir.Split('\\');

            string tmpdir = dirs[0];
            if (!Directory.Exists(tmpdir))
            {
                return (false, $"Das Verzeichnis {tmpdir} existiert nicht");
            }

            for (int i = 1; i < dirs.Length; i++)
            {
                tmpdir += $"\\{dirs[i]}";
                if (!Directory.Exists(tmpdir))
                {
                    try
                    {
                        Directory.CreateDirectory(tmpdir);
                    }
                    catch (Exception ex)
                    {
                        return (false, ex.Message);
                    }
                }

            }

            return (true, "Verzeichnis existiert jetzt");
        }

        public static async Task<bool> LoopWait(Func<bool> checkFunc, int repetitions = 5, int timemoutMs = 2000)
        {
            int currentRep = 1;
            while (currentRep <= repetitions)
            {
                if (checkFunc())
                {
                    return true;
                }
                await Task.Delay(timemoutMs);
                currentRep++;
            }
            return false;
        }

        public static bool IsFileLocked(string file)
        {
            FileInfo info = new FileInfo(file);
            try
            {
                using (FileStream stream = info.Open(FileMode.Open, FileAccess.Read, FileShare.None))
                {
                    stream.Close();
                }
            }
            catch (IOException)
            {
                return true;
            }
            return false;
        }
    }
}
