using System.Text.Json;
using System.Text.Json.Serialization;
using Server.Model;

namespace Server.Data
{
    public static class InvoiceData
    {
        #region Private Properties

        private static string DirectoryName => "Invoices";

        #endregion

        #region Public Method DeleteAsync

        /// <summary>
        ///     Deletes an invoice file asynchronously by its ID.
        /// </summary>
        /// <param name="invoiceId">The ID of the invoice to be deleted.</param>
        /// <return>A task representing the asynchronous delete operation.</return>
        public static async Task DeleteAsync(string invoiceId)
        {
            var fileName = await BuildFileName(invoiceId);
            if (File.Exists(fileName)) File.Delete(fileName);
        }

        #endregion

        #region Public Method ExistsAsync

        /// <summary>
        ///     Checks if an invoice file exists asynchronously by its ID.
        /// </summary>
        /// <param name="invoiceId">The ID of the invoice to check for existence.</param>
        /// <returns>A task representing the asynchronous check operation, with a boolean result indicating whether the file exists.</returns>
        public static async Task<bool> ExistsAsync(string invoiceId) => File.Exists(await BuildFileName(invoiceId));

        #endregion

        #region Public Method LoadAllAsync

        /// <summary>
        ///     Loads all invoices asynchronously.
        /// </summary>
        /// <return>A task representing the asynchronous operation, with a list of <see cref="InvoiceModel" /> as the result.</return>
        public static async Task<List<InvoiceModel>> LoadAllAsync()
        {
            var invoices = new List<InvoiceModel>();

            var invoiceFiles = Directory.GetFiles(await GetDirectoryAsync(), "Invoice_*.json");
            foreach (var file in invoiceFiles)
            {
                var invoice = await LoadFileAsync(file);
                if (invoice != null) invoices.Add(invoice);
            }

            return invoices;
        }

        #endregion

        #region Public Method LoadAsync

        /// <summary>
        ///     Loads an invoice asynchronously by its ID.
        /// </summary>
        /// <param name="invoiceId">The ID of the invoice to be loaded.</param>
        /// <return>The deserialized <see cref="InvoiceModel" /> instance if the file exists; otherwise, null.</return>
        public static async Task<InvoiceModel?> LoadAsync(string invoiceId)
        {
            var fileName = await BuildFileName(invoiceId);
            return await LoadFileAsync(fileName);
        }

        #endregion

        #region Public Method SaveAsync

        /// <summary>
        ///     Saves an invoice asynchronously.
        /// </summary>
        /// <param name="invoice">The invoice model to be saved.</param>
        /// <return>A task representing the asynchronous save operation.</return>
        public static async Task SaveAsync(InvoiceModel invoice)
        {
            // Ensure id is set
            if (string.IsNullOrWhiteSpace(invoice.InvoiceId))
                invoice.InvoiceId = Guid.NewGuid().ToString();

            var jsonString = JsonSerializer.Serialize(invoice, new JsonSerializerOptions { WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull });
            await File.WriteAllTextAsync(await BuildFileName(invoice.InvoiceId), jsonString);
        }

        #endregion
        
        #region Private Method BuildFileName

        private static async Task<string> BuildFileName(string invoiceId)
        {
            var targetDirectory = await GetDirectoryAsync();
            return Path.Combine(targetDirectory, $"Invoice_{invoiceId}.json");
        }

        #endregion

        #region Private Method LoadFileAsync

        private static async Task<InvoiceModel?> LoadFileAsync(string fileName)
        {
            if (!File.Exists(fileName)) return null;
            var jsonString = await File.ReadAllTextAsync(fileName);
            return JsonSerializer.Deserialize<InvoiceModel>(jsonString)!;
        }

        #endregion

        /// <summary>
        /// Ensures that the directory for storing invoices exists asynchronously.
        /// </summary>
        /// <return>A task representing the asynchronous operation to ensure the directory exists.</return>
        private static Task<string> GetDirectoryAsync()
        {
            var parentDirectory = Directory.GetParent(Directory.GetCurrentDirectory());
            if (parentDirectory == null) return Task.FromResult(string.Empty);
            var targetDirectory = Path.Combine(parentDirectory.FullName, DirectoryName);
            
            if(!Directory.Exists(targetDirectory)) Directory.CreateDirectory(targetDirectory);
            return Task.FromResult(targetDirectory);
        }
    }
}