// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.IO;
using System.Reflection;

namespace Microsoft.Extensions.FileProviders.Embedded;

/// <summary>
/// Represents a file embedded in an assembly.
/// </summary>
public class EmbeddedResourceFileInfo : IFileInfo
{
    private readonly Assembly _assembly;
    private readonly string _resourcePath;

    private long? _length;

    /// <summary>
    /// Initializes a new instance of <see cref="EmbeddedFileProvider"/> for an assembly using <paramref name="resourcePath"/> as the base
    /// </summary>
    /// <param name="assembly">The assembly that contains the embedded resource</param>
    /// <param name="resourcePath">The path to the embedded resource</param>
    /// <param name="name">An arbitrary name for this instance</param>
    /// <param name="lastModified">The <see cref="DateTimeOffset" /> to use for <see cref="LastModified" /></param>
    public EmbeddedResourceFileInfo(
        Assembly assembly,
        string resourcePath,
        string name,
        DateTimeOffset lastModified)
    {
        _assembly = assembly;
        _resourcePath = resourcePath;
        Name = name;
        LastModified = lastModified;
    }

    /// <summary>
    /// Always true.
    /// </summary>
    public bool Exists => true;

    /// <summary>
    /// The length, in bytes, of the embedded resource
    /// </summary>
    public long Length
    {
        get
        {
            if (!_length.HasValue)
            {
                using var stream = GetManifestResourceStream();
                _length = stream.Length;
            }
            return _length.Value;
        }
    }

    /// <summary>
    /// Always null.
    /// </summary>
    public string? PhysicalPath => null;

    /// <summary>
    /// The name of embedded file
    /// </summary>
    public string Name { get; }

    /// <summary>
    /// The time, in UTC, when the <see cref="EmbeddedFileProvider"/> was created
    /// </summary>
    public DateTimeOffset LastModified { get; }

    /// <summary>
    /// Always false.
    /// </summary>
    public bool IsDirectory => false;

    /// <inheritdoc />
    public Stream CreateReadStream()
    {
        var stream = GetManifestResourceStream();
        if (!_length.HasValue)
        {
            _length = stream.Length;
        }

        return stream;
    }

    private Stream GetManifestResourceStream()
    {
        var stream = _assembly.GetManifestResourceStream(_resourcePath);
        if (stream == null)
        {
            throw new InvalidOperationException($"Couldn't get resource at '{_resourcePath}'.");
        }

        return stream;
    }
}
