using Android.Content.Res;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Primitives;

namespace Hua.Todo.Maui.Platforms.Android;

public sealed class AndroidAssetFileProvider : IFileProvider
{
	private readonly AssetManager _assets;
	private readonly string _root;

	public AndroidAssetFileProvider(AssetManager assets, string root)
	{
		_assets = assets;
		_root = NormalizePath(root).TrimEnd('/');
	}

	public IFileInfo GetFileInfo(string subpath)
	{
		var assetPath = Combine(_root, subpath);
		if (string.IsNullOrEmpty(assetPath))
		{
			return new NotFoundFileInfo(subpath);
		}

		try
		{
			using var stream = _assets.Open(assetPath);
			return new AndroidAssetFileInfo(_assets, assetPath, Path.GetFileName(assetPath), false);
		}
		catch (global::Java.IO.FileNotFoundException)
		{
			return new NotFoundFileInfo(subpath);
		}
		catch
		{
			return new NotFoundFileInfo(subpath);
		}
	}

	public IDirectoryContents GetDirectoryContents(string subpath)
	{
		var dirPath = Combine(_root, subpath).TrimEnd('/');
		if (string.IsNullOrEmpty(dirPath))
		{
			return NotFoundDirectoryContents.Singleton;
		}

		try
		{
			var entries = _assets.List(dirPath) ?? Array.Empty<string>();
			return new AndroidAssetDirectoryContents(_assets, dirPath, entries);
		}
		catch
		{
			return NotFoundDirectoryContents.Singleton;
		}
	}

	public IChangeToken Watch(string filter) => NullChangeToken.Singleton;

	private static string Combine(string root, string subpath)
	{
		var cleanSub = NormalizePath(subpath).TrimStart('/');
		if (string.IsNullOrEmpty(root)) return cleanSub;
		if (string.IsNullOrEmpty(cleanSub)) return root;
		return $"{root}/{cleanSub}";
	}

	private static string NormalizePath(string path) => (path ?? string.Empty).Replace('\\', '/');

	private sealed class AndroidAssetFileInfo : IFileInfo
	{
		private readonly AssetManager _assets;
		private readonly string _assetPath;

		public AndroidAssetFileInfo(AssetManager assets, string assetPath, string name, bool isDirectory)
		{
			_assets = assets;
			_assetPath = assetPath;
			Name = name;
			IsDirectory = isDirectory;
		}

		public bool Exists => true;
		public long Length => -1;
		public string? PhysicalPath => null;
		public string Name { get; }
		public DateTimeOffset LastModified => DateTimeOffset.MinValue;
		public bool IsDirectory { get; }

		public Stream CreateReadStream() => _assets.Open(_assetPath);
	}

	private sealed class AndroidAssetDirectoryContents : IDirectoryContents
	{
		private readonly AssetManager _assets;
		private readonly string _dirPath;
		private readonly string[] _entries;

		public AndroidAssetDirectoryContents(AssetManager assets, string dirPath, string[] entries)
		{
			_assets = assets;
			_dirPath = dirPath;
			_entries = entries;
		}

		public bool Exists => true;

		public IEnumerator<IFileInfo> GetEnumerator()
		{
			foreach (var entry in _entries)
			{
				if (string.IsNullOrEmpty(entry)) continue;

				var childPath = $"{_dirPath}/{entry}";
				var childList = Array.Empty<string>();
				var isDir = false;

				try
				{
					childList = _assets.List(childPath) ?? Array.Empty<string>();
					isDir = childList.Length > 0;
				}
				catch
				{
				}

				yield return new AndroidAssetFileInfo(_assets, childPath, entry, isDir);
			}
		}

		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();
	}
}
