using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using JetBrains.Annotations;

namespace StabilityMatrix.Core.Models.FileInterfaces;

[PublicAPI]
[Localizable(false)]
public class FileSystemPath : IEquatable<FileSystemPath>, IFormattable
{
    public string FullPath { get; }

    protected FileSystemPath(string path)
    {
        FullPath = path;
    }

    protected FileSystemPath(FileSystemPath path)
        : this(path.FullPath) { }

    protected FileSystemPath(params string[] paths)
        : this(Path.Combine(paths)) { }

    /// <inheritdoc />
    public override string ToString()
    {
        return FullPath;
    }

    /// <inheritdoc />
    string IFormattable.ToString(string? format, IFormatProvider? formatProvider)
    {
        return ToString(format, formatProvider);
    }

    /// <summary>
    /// Overridable IFormattable.ToString method.
    /// By default, returns <see cref="FullPath"/>.
    /// </summary>
    protected virtual string ToString(string? format, IFormatProvider? formatProvider)
    {
        return FullPath;
    }

    public static bool operator ==(FileSystemPath? left, FileSystemPath? right)
    {
        return Equals(left, right);
    }

    public static bool operator !=(FileSystemPath? left, FileSystemPath? right)
    {
        return !Equals(left, right);
    }

    /// <inheritdoc />
    public bool Equals(FileSystemPath? other)
    {
        if (ReferenceEquals(null, other))
            return false;
        if (ReferenceEquals(this, other))
            return true;

        return string.Equals(
            GetNormalizedPath(FullPath),
            GetNormalizedPath(other.FullPath),
            OperatingSystem.IsWindows() ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal
        );
    }

    /// <inheritdoc />
    public override bool Equals(object? obj)
    {
        if (ReferenceEquals(null, obj))
            return false;
        if (ReferenceEquals(this, obj))
            return true;
        if (GetType() != obj.GetType())
            return false;
        return Equals((FileSystemPath)obj);
    }

    /// <summary>
    /// Normalize a path to a consistent format for comparison.
    /// </summary>
    /// <param name="path">Path to normalize.</param>
    /// <returns>Normalized path.</returns>
    [return: NotNullIfNotNull(nameof(path))]
    private static string? GetNormalizedPath(string? path)
    {
        // Return null or empty paths as-is
        if (string.IsNullOrEmpty(path))
        {
            return path;
        }

        if (Uri.TryCreate(path, UriKind.RelativeOrAbsolute, out var uri))
        {
            if (uri.IsAbsoluteUri)
            {
                path = uri.LocalPath;
            }
        }

        // Get full path if possible, ignore errors like invalid chars or too long
        try
        {
            path = Path.GetFullPath(path);
        }
        catch (SystemException) { }

        return path.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
    }

    /// <inheritdoc />
    public override int GetHashCode()
    {
        return HashCode.Combine(GetType().GetHashCode(), FullPath.GetHashCode());
    }

    // Implicit conversions to and from string
    public static implicit operator string(FileSystemPath path) => path.FullPath;

    public static implicit operator FileSystemPath(string path) => new(path);
}
