using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;

namespace EonaCat.LogStack.Helpers
{
    /// <summary>
    /// Utility class for managing directory permissions and ensuring write access.
    /// Handles both Windows and Unix-like systems gracefully.
    /// </summary>
    public static class DirectoryPermissionHelper
    {
        /// <summary>
        /// Checks if the current process has write access to the specified directory.
        /// Returns true if writable, false otherwise. Never throws.
        /// </summary>
        public static bool CanWrite(string dirPath)
        {
            if (string.IsNullOrEmpty(dirPath))
            {
                return false;
            }

            try
            {
                if (!Directory.Exists(dirPath))
                {
                    // Check if we can create it
                    var parentPath = Path.GetDirectoryName(dirPath);
                    if (string.IsNullOrEmpty(parentPath) || parentPath == dirPath)
                    {
                        return false;
                    }
                    return CanWrite(parentPath);
                }

                // Try to create a temporary file to verify write access
                var testFile = Path.Combine(dirPath, ".permission_test_" + Guid.NewGuid().ToString("N"));
                try
                {
                    using (var fs = File.Create(testFile, 1, FileOptions.DeleteOnClose))
                    {
                        fs.WriteByte(0);
                    }
                    return true;
                }
                catch
                {
                    return false;
                }
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// Attempts to create a directory and fix permissions if needed.
        /// Never throws - logs errors and returns false if it cannot fix permissions.
        /// </summary>
        public static bool EnsureDirectory(string dirPath)
        {
            if (string.IsNullOrEmpty(dirPath))
            {
                return false;
            }

            try
            {
                // Create directory if it doesn't exist
                if (!Directory.Exists(dirPath))
                {
                    Directory.CreateDirectory(dirPath);
                }

                // Verify we have write access
                if (CanWrite(dirPath))
                {
                    return true;
                }

                // Try to fix permissions (Windows-specific via attribute clearing)
                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                {
                    try
                    {
                        TryFixWindowsPermissions(dirPath);
                        return CanWrite(dirPath);
                    }
                    catch
                    {
                        // Permission fixing failed, but that's okay - we tried
                    }
                }

                // For Unix-like systems, we can't easily fix permissions without external tools
                // Just return whether we can write
                return CanWrite(dirPath);
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// Attempts to ensure all parent directories have write access.
        /// Creates missing directories.
        /// </summary>
        public static bool EnsureDirectoryHierarchy(string dirPath)
        {
            if (string.IsNullOrEmpty(dirPath))
            {
                return false;
            }

            try
            {
                var current = Path.GetDirectoryName(dirPath);
                var stack = new System.Collections.Generic.Stack<string>();

                // Build directory hierarchy
                while (!string.IsNullOrEmpty(current) && current != Path.GetPathRoot(current))
                {
                    if (Directory.Exists(current))
                    {
                        break;
                    }
                    stack.Push(current);
                    current = Path.GetDirectoryName(current);
                }

                // Create missing directories from root to target
                while (stack.Count > 0)
                {
                    var dir = stack.Pop();
                    if (!Directory.Exists(dir))
                    {
                        Directory.CreateDirectory(dir);
                    }
                }

                // Ensure final target directory
                return EnsureDirectory(dirPath);
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// Attempts to set full permissions for everyone on the directory.
        /// Falls back to clearing the read-only attribute if needed on Windows.
        /// Uses chmod on Linux/Mac and Windows permission APIs on Windows.
        /// This is a best-effort operation for all platforms.
        /// </summary>
        public static bool TrySetEveryonePermissions(string dirPath)
        {
            if (string.IsNullOrEmpty(dirPath) || !Directory.Exists(dirPath))
            {
                return false;
            }

            try
            {
                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                {
                    return TrySetWindowsPermissions(dirPath);
                }
                else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
                {
                    return TrySetUnixPermissions(dirPath);
                }

                // Unknown platform - try whatever we can
                return CanWrite(dirPath);
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// Attempts to set Windows directory permissions by clearing the read-only attribute.
        /// </summary>
        private static bool TrySetWindowsPermissions(string dirPath)
        {
            try
            {
                var dirInfo = new DirectoryInfo(dirPath);

                // Clear read-only attribute if set
                if ((dirInfo.Attributes & FileAttributes.ReadOnly) != 0)
                {
                    try
                    {
                        dirInfo.Attributes &= ~FileAttributes.ReadOnly;
                    }
                    catch
                    {
                        // Continue even if attribute clearing fails
                    }
                }

                // Verify the directory is now writable
                return CanWrite(dirPath);
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// Attempts to set Unix (Linux/Mac) directory permissions using chmod.
        /// Sets full permissions (777) for the directory.
        /// </summary>
        private static bool TrySetUnixPermissions(string dirPath)
        {
            if (string.IsNullOrEmpty(dirPath))
            {
                return false;
            }

            try
            {
                // Use chmod to set full permissions (777)
                var process = new Process
                {
                    StartInfo = new ProcessStartInfo
                    {
                        FileName = "chmod",
                        Arguments = $"777 \"{dirPath}\"",
                        UseShellExecute = false,
                        RedirectStandardOutput = true,
                        RedirectStandardError = true,
                        CreateNoWindow = true
                    }
                };

                process.Start();
                process.WaitForExit(5000); // Wait up to 5 seconds

                if (process.ExitCode == 0)
                {
                    // Verify the directory is now writable
                    return CanWrite(dirPath);
                }

                return false;
            }
            catch
            {
                // chmod not available or failed
                return false;
            }
        }

        /// <summary>
        /// Attempts to grant write permissions on Windows by clearing the read-only attribute.
        /// This is a best-effort operation and may fail on restricted systems.
        /// </summary>
        private static void TryFixWindowsPermissions(string dirPath)
        {
            try
            {
                var dirInfo = new DirectoryInfo(dirPath);

                // Clear read-only attribute if set
                if ((dirInfo.Attributes & FileAttributes.ReadOnly) != 0)
                {
                    dirInfo.Attributes &= ~FileAttributes.ReadOnly;
                }

                // Note: Full ACL manipulation would require System.Security.AccessControl
                // which is a separate NuGet package. We're using the basic attribute approach
                // which covers most common permission issues.
            }
            catch
            {
                // Silently fail - we tried our best
                throw;
            }
        }

        /// <summary>
        /// Gets a safe description of why directory access failed.
        /// </summary>
        public static string GetAccessIssueDiagnosis(string dirPath)
        {
            try
            {
                if (string.IsNullOrEmpty(dirPath))
                {
                    return "Directory path is null or empty";
                }

                if (!Directory.Exists(dirPath))
                {
                    var parent = Path.GetDirectoryName(dirPath);
                    if (string.IsNullOrEmpty(parent))
                    {
                        return $"Cannot determine parent directory: {dirPath}";
                    }
                    if (!Directory.Exists(parent))
                    {
                        return $"Parent directory does not exist: {parent}";
                    }
                    return $"Directory does not exist and parent is not writable: {dirPath}";
                }

                var dirInfo = new DirectoryInfo(dirPath);

                if ((dirInfo.Attributes & FileAttributes.ReadOnly) != 0)
                {
                    return $"Directory is read-only: {dirPath}";
                }

                return $"Directory exists but no write permission: {dirPath}. Check file system permissions.";
            }
            catch (Exception ex)
            {
                return $"Error diagnosing directory: {ex.Message}";
            }
        }
    }
}
