﻿using System;
using System.IO;
using System.IO.Compression;
using System.Threading.Tasks;


namespace Ghul.IO
{
	/// <summary>
	/// A simple implementation for <see cref="IFileHandle"/> based on
	/// <see cref="ZipArchiveEntry"/>.
	/// </summary>
	internal class ZipArchiveFileHandle : IFileHandle
	{
		/// <summary>
		/// The ID associated with the file.
		/// </summary>
		public string Id { get; }


		// TODO: Potential FIXME: Uses constants as archive immutability is assumed!

		/// <summary>
		/// Indicates if the file currently exists.
		/// </summary>
		/// <remarks>Cached information, remains constant until <see cref="RefreshAsync"/> is called.</remarks>
		public bool Exists => true;

		/// <summary>
		/// The name of the file including its extension (null if the file does not currently exist).
		/// </summary>
		/// <remarks>Cached information, remains constant until <see cref="RefreshAsync"/> is called.</remarks>
		public string Name => entry.Name;

		/// <summary>
		/// The extension of the file (the file name substring after the last ".", null if the file
		/// does not currently exist).
		/// </summary>
		/// <remarks>Cached information, remains constant until <see cref="RefreshAsync"/> is called.</remarks>
		public string Extension => entry.Name.Substring(entry.Name.LastIndexOf('.') + 1);

		/// <summary>
		/// The last write time of the file (null if the file does not currently exist).
		/// </summary>
		/// <remarks>Cached information, remains constant until <see cref="RefreshAsync"/> is called.</remarks>
		public DateTimeOffset? LastModified => entry.LastWriteTime;


		/// <summary>
		/// The entry associated with the handle.
		/// </summary>
		private ZipArchiveEntry entry;


		/// <summary>
		/// Creates a new file handle based on <see cref="ZipArchiveEntry"/>.
		/// </summary>
		/// <param name="id">The ID associated with this handle.</param>
		/// <param name="entry">The entry associated with the handle.</param>
		/// <exception cref="ArgumentException">If id is null or empty.</exception>
		/// <exception cref="ArgumentNullException">If entry is null.</exception>
		public ZipArchiveFileHandle(string id, ZipArchiveEntry entry)
		{
			Ensure.IsNotNullOrEmpty(id, nameof(id));
			Ensure.IsNotNull(entry, nameof(entry));

			Id = id;

			this.entry = entry;
		}


		/// <summary>
		/// Asynchronously refreshes the cached file information.
		/// </summary>
		/// <returns>A task representing the refresh.</returns>
		public Task RefreshAsync()
		{
			// TODO: Potential FIXME: This does nothing as archive immutability is assumed!

			return Task.CompletedTask;
		}

		/// <summary>
		/// Asynchronously opens a read-only stream to the file. The stream does not have to be
		/// seekable or support asynchronous reading but should whenever possible.
		/// </summary>
		/// <returns>
		/// A task resulting in an opened read-only stream to the file (stream will be null if the
		/// file does not currently exist).
		/// </returns>
		public Task<Stream> OpenReadAsync()
		{
			// TODO: Potential FIXME: This will never return null as archive immutability is assumed!

			// Zip streams are not seekable and do not support asynchronous reading by default
			return Task.FromResult(entry.Open());
		}
	}
}
