using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Abstractions;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Quadarax.Foundation.Core.IO.FileSystem.Memory
{


    public class MemoryDirectoryInfo : IDirectoryInfo
    {
        private readonly MemoryFileSystem _fileSystem;
        private string _path;

        public MemoryDirectoryInfo(MemoryFileSystem fileSystem, string path)
        {
            _fileSystem = fileSystem;
            _path = path;
        }

        public FileAttributes Attributes
    {
        get
        {
            if (_fileSystem.FileSystemDictionary.TryGetValue(_path, out var entry))
            {
                return entry.Attributes;
            }
            throw new DirectoryNotFoundException($"Directory not found: {_path}");
        }
        set
        {
            if (_fileSystem.FileSystemDictionary.TryGetValue(_path, out var entry))
            {
                entry.Attributes = value;
            }
            else
            {
                throw new DirectoryNotFoundException($"Directory not found: {_path}");
            }
        }
    }

    public DateTime CreationTime
    {
        get
        {
            if (_fileSystem.FileSystemDictionary.TryGetValue(_path, out var entry))
            {
                return entry.CreationTime;
            }
            throw new DirectoryNotFoundException($"Directory not found: {_path}");
        }
        set
        {
            if (_fileSystem.FileSystemDictionary.TryGetValue(_path, out var entry))
            {
                entry.CreationTime = value;
            }
            else
            {
                throw new DirectoryNotFoundException($"Directory not found: {_path}");
            }
        }
    }

    public string FullName => _path;

    public string Name => Path.GetFileName(_path);

    public IDirectoryInfo? Parent
    {
        get
        {
            string? parentPath = Path.GetDirectoryName(_path);
            return parentPath != null ? new MemoryDirectoryInfo(_fileSystem, parentPath) : null;
        }
    }

    public bool Exists => _fileSystem.FileSystemDictionary.ContainsKey(_path);

        public string Extension => Path.GetExtension(_path);

      

        public DateTime LastAccessTime
        {
            get
            {
                if (_fileSystem.FileSystemDictionary.TryGetValue(_path, out var entry))
                {
                    return entry.LastAccessTime;
                }
                throw new DirectoryNotFoundException($"Directory not found: {_path}");
            }
            set
            {
                if (_fileSystem.FileSystemDictionary.TryGetValue(_path, out var entry))
                {
                    entry.LastAccessTime = value;
                }
                else
                {
                    throw new DirectoryNotFoundException($"Directory not found: {_path}");
                }
            }
        }

        public DateTime LastAccessTimeUtc
        {
            get => LastAccessTime.ToUniversalTime();
            set => LastAccessTime = value.ToLocalTime();
        }

        public DateTime LastWriteTime
        {
            get
            {
                if (_fileSystem.FileSystemDictionary.TryGetValue(_path, out var entry))
                {
                    return entry.LastWriteTime;
                }
                throw new DirectoryNotFoundException($"Directory not found: {_path}");
            }
            set
            {
                if (_fileSystem.FileSystemDictionary.TryGetValue(_path, out var entry))
                {
                    entry.LastWriteTime = value;
                }
                else
                {
                    throw new DirectoryNotFoundException($"Directory not found: {_path}");
                }
            }
        }

        public DateTime LastWriteTimeUtc
        {
            get => LastWriteTime.ToUniversalTime();
            set => LastWriteTime = value.ToLocalTime();
        }

      
     

        public IDirectoryInfo Root => new MemoryDirectoryInfo(_fileSystem, Path.GetPathRoot(_path) ?? string.Empty);

        public IFileSystem FileSystem => throw new NotImplementedException();

        public string? LinkTarget => throw new NotImplementedException();

           public UnixFileMode UnixFileMode
    {
        get
        {
            if (_fileSystem.FileSystemDictionary.TryGetValue(_path, out var entry))
            {
                return entry.UnixFileMode;
            }
            throw new DirectoryNotFoundException($"Directory not found: {_path}");
        }
        set
        {
            if (_fileSystem.FileSystemDictionary.TryGetValue(_path, out var entry))
            {
                entry.UnixFileMode = value;
            }
            else
            {
                throw new DirectoryNotFoundException($"Directory not found: {_path}");
            }
        }
    }
        public DateTime CreationTimeUtc { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }

        public void Create()
    {
        _fileSystem.Directory.CreateDirectory(_path);
    }

    public void Delete()
    {
        _fileSystem.Directory.Delete(_path);
    }

        public void CreateAsSymbolicLink(string pathToTarget)
        {
            if (string.IsNullOrEmpty(pathToTarget))
            {
                throw new ArgumentNullException(nameof(pathToTarget));
            }

            if (_fileSystem.FileSystemDictionary.ContainsKey(_path))
            {
                throw new IOException($"Cannot create '{_path}' because a file or directory with the same name already exists.");
            }

            // Normalize the target path
            string normalizedTargetPath = _fileSystem.Path.GetFullPath(pathToTarget);

            // Check if the target exists (optional, remove if you want to allow dangling symlinks)
            if (!_fileSystem.FileSystemDictionary.ContainsKey(normalizedTargetPath))
            {
                throw new DirectoryNotFoundException($"Target directory not found: {normalizedTargetPath}");
            }

            // Create the symbolic link
            var symlinkEntry = new MemoryDirectoryEntry
            {
                Name = _fileSystem.Path.GetFileName(_path),
                FullName = _path,
                CreationTime = DateTime.Now,
                LastAccessTime = DateTime.Now,
                LastWriteTime = DateTime.Now,
                Attributes = FileAttributes.Directory | FileAttributes.ReparsePoint,
                IsSymbolicLink = true,
                SymbolicLinkTarget = normalizedTargetPath
            };

            _fileSystem.FileSystemDictionary[_path] = symlinkEntry;

            // Add the symlink to its parent directory's children
            string? parentPath = _fileSystem.Path.GetDirectoryName(_path);
            if (_fileSystem.FileSystemDictionary.TryGetValue(parentPath ?? string.Empty, out var parentEntry) && parentEntry is MemoryDirectoryEntry parentDir)
            {
                parentDir.Children[symlinkEntry.Name] = symlinkEntry;
            }
        }

        public IDirectoryInfo CreateSubdirectory(string path)
        {
            return _fileSystem.Directory.CreateDirectory(Path.Combine(_path, path));
        }

      
        public void Delete(bool recursive)
        {
            _fileSystem.Directory.Delete(_path, recursive);
        }

        public IEnumerable<IDirectoryInfo> EnumerateDirectories()
        {
            return _fileSystem.Directory.EnumerateDirectories(_path).Select(dir => new MemoryDirectoryInfo(_fileSystem, dir));
        }

        public IEnumerable<IDirectoryInfo> EnumerateDirectories(string searchPattern)
        {
            return _fileSystem.Directory.EnumerateDirectories(_path, searchPattern).Select(dir => new MemoryDirectoryInfo(_fileSystem, dir));
        }

        public IEnumerable<IDirectoryInfo> EnumerateDirectories(string searchPattern, SearchOption searchOption)
        {
            return _fileSystem.Directory.EnumerateDirectories(_path, searchPattern, searchOption).Select(dir => new MemoryDirectoryInfo(_fileSystem, dir));
        }

        public IEnumerable<IDirectoryInfo> EnumerateDirectories(string searchPattern, EnumerationOptions enumerationOptions)
        {
            return _fileSystem.Directory.EnumerateDirectories(_path, searchPattern, enumerationOptions)
                .Select(dir => new MemoryDirectoryInfo(_fileSystem, dir));
        }

        public IEnumerable<IFileInfo> EnumerateFiles()
        {
            return _fileSystem.Directory.EnumerateFiles(_path).Select(file => new MemoryFileInfo(_fileSystem, file));
        }

        public IEnumerable<IFileInfo> EnumerateFiles(string searchPattern)
        {
            return _fileSystem.Directory.EnumerateFiles(_path, searchPattern).Select(file => new MemoryFileInfo(_fileSystem, file));
        }

        public IEnumerable<IFileInfo> EnumerateFiles(string searchPattern, SearchOption searchOption)
        {
            return _fileSystem.Directory.EnumerateFiles(_path, searchPattern, searchOption).Select(file => new MemoryFileInfo(_fileSystem, file));
        }

        public IEnumerable<IFileInfo> EnumerateFiles(string searchPattern, EnumerationOptions enumerationOptions)
        {
            return _fileSystem.Directory.EnumerateFiles(_path, searchPattern, enumerationOptions)
                .Select(file => new MemoryFileInfo(_fileSystem, file));
        }

        public IEnumerable<IFileSystemInfo> EnumerateFileSystemInfos()
        {
            return _fileSystem.Directory.EnumerateFileSystemEntries(_path).Select(entry =>
                _fileSystem.FileSystemDictionary[entry] is MemoryDirectoryEntry
                    ? (IFileSystemInfo)new MemoryDirectoryInfo(_fileSystem, entry)
                    : new MemoryFileInfo(_fileSystem, entry));
        }

        public IEnumerable<IFileSystemInfo> EnumerateFileSystemInfos(string searchPattern)
        {
            return _fileSystem.Directory.EnumerateFileSystemEntries(_path, searchPattern).Select(entry =>
                _fileSystem.FileSystemDictionary[entry] is MemoryDirectoryEntry
                    ? (IFileSystemInfo)new MemoryDirectoryInfo(_fileSystem, entry)
                    : new MemoryFileInfo(_fileSystem, entry));
        }

        public IEnumerable<IFileSystemInfo> EnumerateFileSystemInfos(string searchPattern, SearchOption searchOption)
        {
            return _fileSystem.Directory.EnumerateFileSystemEntries(_path, searchPattern, searchOption).Select(entry =>
                _fileSystem.FileSystemDictionary[entry] is MemoryDirectoryEntry
                    ? (IFileSystemInfo)new MemoryDirectoryInfo(_fileSystem, entry)
                    : new MemoryFileInfo(_fileSystem, entry));
        }

        public IEnumerable<IFileSystemInfo> EnumerateFileSystemInfos(string searchPattern, EnumerationOptions enumerationOptions)
        {
            return _fileSystem.Directory.EnumerateFileSystemEntries(_path, searchPattern, enumerationOptions)
                .Select(entry => CreateFileSystemInfo(entry));
        }

        private IFileSystemInfo CreateFileSystemInfo(string path)
        {
            if (_fileSystem.FileSystemDictionary.TryGetValue(path, out var entry))
            {
                if (entry is MemoryDirectoryEntry)
                {
                    return new MemoryDirectoryInfo(_fileSystem, path);
                }
                else if (entry is MemoryFileEntry)
                {
                    return new MemoryFileInfo(_fileSystem, path);
                }
            }

            // This should not happen in a properly maintained in-memory file system,
            // but we'll handle it just in case
            throw new FileNotFoundException($"File or directory not found: {path}");
        }

        public IDirectoryInfo[] GetDirectories()
        {
            return EnumerateDirectories().ToArray();
        }

        public IDirectoryInfo[] GetDirectories(string searchPattern)
        {
            return EnumerateDirectories(searchPattern).ToArray();
        }

        public IDirectoryInfo[] GetDirectories(string searchPattern, SearchOption searchOption)
        {
            return EnumerateDirectories(searchPattern, searchOption).ToArray();
        }

        public IDirectoryInfo[] GetDirectories(string searchPattern, EnumerationOptions enumerationOptions)
        {
            throw new NotImplementedException();
        }

        public IFileInfo[] GetFiles()
        {
            return EnumerateFiles().ToArray();
        }

        public IFileInfo[] GetFiles(string searchPattern)
        {
            return EnumerateFiles(searchPattern).ToArray();
        }

        public IFileInfo[] GetFiles(string searchPattern, SearchOption searchOption)
        {
            return EnumerateFiles(searchPattern, searchOption).ToArray();
        }

        public IFileInfo[] GetFiles(string searchPattern, EnumerationOptions enumerationOptions)
        {
            throw new NotImplementedException();
        }

        public IFileSystemInfo[] GetFileSystemInfos()
        {
            return EnumerateFileSystemInfos().ToArray();
        }

        public IFileSystemInfo[] GetFileSystemInfos(string searchPattern)
        {
            return EnumerateFileSystemInfos(searchPattern).ToArray();
        }

        public IFileSystemInfo[] GetFileSystemInfos(string searchPattern, SearchOption searchOption)
        {
            return EnumerateFileSystemInfos(searchPattern, searchOption).ToArray();
        }

        public IFileSystemInfo[] GetFileSystemInfos(string searchPattern, EnumerationOptions enumerationOptions)
        {
            throw new NotImplementedException();
        }

        public void MoveTo(string destDirName)
        {
            _fileSystem.Directory.Move(_path, destDirName);
            _path = destDirName;
        }

        public void Refresh()
        {
            // In an in-memory file system, this method doesn't need to do anything
            // as the information is always up-to-date
        }

        public IFileSystemInfo? ResolveLinkTarget(bool returnFinalTarget)
        {
            throw new NotImplementedException();
        }

        public FileSystemInfo ToFileSystemInfo()
        {
            throw new NotImplementedException("Converting to FileSystemInfo is not supported in the in-memory file system.");
        }
    }
}
