using ExtensiblePortfolioSite.SDK.Resources;
using ExtensiblePortfolioSite.SDK.Resources.VFS;

using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Primitives;

using System.Collections;
using System.Text;

namespace ExtensiblePortfolioSite
{
	public class VFSFileProvider : IFileProvider
	{
		private class VFS_FileInfo_File : IFileInfo
		{
			private readonly IVirtualFile File;
			public VFS_FileInfo_File(IVirtualFile File) => this.File = File;
			
			public bool Exists => true;
			public bool IsDirectory => false;

			public DateTimeOffset LastModified => DateTimeOffset.UnixEpoch;
			public long Length
			{
				get
				{
					if (this.File is VirtualFile VF)
						if(VF.ResourceContainer is LocalFile LocalFile)
							return new FileInfo(LocalFile.LocalFilePath).Length;

					using Stream STM = this.File.OpenStream();
					return STM.Length;
				}
			}
			public string Name => File.Name;

			public string PhysicalPath
			{
				get
				{
					StringBuilder Builder = new(256);
					Stack<IVirtualFolder> Folders = new();

					IVirtualFolder? Current = File.Folder;
					while(Current != null)
					{
						Folders.Push(Current);
						Current = Current.Parent;
					}

					while (Folders.TryPop(out Current))
					{
						Builder.Append(Current.Foldername);
						Builder.Append(Path.DirectorySeparatorChar);
					}

					Builder.Append(File.Filename);
					return Builder.ToString();
				}
			}

			public Stream CreateReadStream() => File.OpenStream();
		}
        private class VFS_FileInfo_Folder : IFileInfo
        {
            private readonly IVirtualFolder Folder;
            public VFS_FileInfo_Folder(IVirtualFolder File) => this.Folder = File;

            public bool Exists => true;
            public bool IsDirectory => true;

            public DateTimeOffset LastModified => DateTimeOffset.UnixEpoch;
            public long Length => -1;
            public string Name => Folder.Name;

            public string PhysicalPath
            {
                get
                {
                    StringBuilder Builder = new(256);
                    Stack<IVirtualFolder> Folders = new();

                    IVirtualFolder? Current = Folder.Parent;
                    while (Current != null)
                    {
                        Folders.Push(Current);
                        Current = Current.Parent;
                    }

                    while (Folders.TryPop(out Current))
                    {
                        Builder.Append(Current);
                        Builder.Append(Path.DirectorySeparatorChar);
                    }

                    Builder.Append(Folder.Foldername);
                    return Builder.ToString();
                }
            }

            public Stream CreateReadStream() => throw new InvalidOperationException("Can't Open Folder as File");
        }
        private class VFS_FileInfo_NULL : IFileInfo
        {
            public VFS_FileInfo_NULL(String PhysicalPath) => this.PhysicalPath = PhysicalPath;

            public bool Exists => false;
            public bool IsDirectory => false;

            public DateTimeOffset LastModified => DateTimeOffset.MinValue;
            public long Length => -1;
			public string Name => Path.GetFileName(PhysicalPath)!;

            public string PhysicalPath { get; }

            public Stream CreateReadStream() => throw new InvalidOperationException("Can't Open Non-Existant File!");
        }


		private class VFS_DirectoryContents : IDirectoryContents
		{
			private readonly IVirtualFolder? Folder;
			public VFS_DirectoryContents(IVirtualFolder? Folder) => this.Folder = Folder;

			public bool Exists => this.Folder != null;

			public IEnumerator<IFileInfo> GetEnumerator()
			{
				if(this.Folder != null)
					foreach(IVirtualEntry Entry in this.Folder.EnumerateEntries())
					{
						switch(Entry.Kind)
						{
							case VirtualEntryKind.File:
								yield return new VFS_FileInfo_File((IVirtualFile)Entry);
								break;

							case VirtualEntryKind.Folder:
								yield return new VFS_FileInfo_Folder((IVirtualFolder)Entry);
								break;

							default:
								continue;
						}
					}
			}
			IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
		}

		public IDirectoryContents GetDirectoryContents(string subpath) => new VFS_DirectoryContents(ResourceManager.GetVirtualFolder($"/wwwroot{subpath}"));

		public IFileInfo GetFileInfo(string subpath)
		{
			subpath = $"/wwwroot{subpath}";

			IVirtualEntry? Entry = ResourceManager.GetVirtualEntry(subpath);

			if (Entry == null)
				return new VFS_FileInfo_NULL(subpath);

			switch(Entry.Kind)
			{
				case VirtualEntryKind.File:
					return new VFS_FileInfo_File((IVirtualFile)Entry);
				case VirtualEntryKind.Folder:
					return new VFS_FileInfo_Folder((IVirtualFolder)Entry);
				default:
					return new VFS_FileInfo_NULL(subpath);
			}
		}

		public IChangeToken Watch(string filter)
		{
			throw new NotSupportedException();
		}
	}
}
