using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;

namespace JobTrackerApi.Services;

public sealed class ExternalOrigin
{
    private static readonly HashSet<string> InternalHealthHosts = new(StringComparer.OrdinalIgnoreCase)
    {
        "backend",
        "localhost",
        "127.0.0.1",
        "::1",
    };

    private readonly Uri _uri;

    private ExternalOrigin(Uri uri)
    {
        _uri = uri;
        BaseUrl = uri.GetComponents(UriComponents.SchemeAndServer, UriFormat.UriEscaped).TrimEnd('/');
    }

    public string BaseUrl { get; }
    public bool UsesHttps => _uri.Scheme == Uri.UriSchemeHttps;

    public static ExternalOrigin FromConfiguration(IConfiguration configuration, bool production = false) =>
        Parse(configuration["App:PublicBaseUrl"], production);

    public static ExternalOrigin Parse(string? value, bool production)
    {
        var raw = value?.Trim();
        if (string.IsNullOrWhiteSpace(raw))
        {
            if (production)
                throw new InvalidOperationException("App:PublicBaseUrl is required in Production.");

            raw = "http://localhost:3000";
        }

        if (!Uri.TryCreate(raw, UriKind.Absolute, out var uri)
            || (uri.Scheme != Uri.UriSchemeHttp && uri.Scheme != Uri.UriSchemeHttps)
            || string.IsNullOrWhiteSpace(uri.Host)
            || !string.IsNullOrEmpty(uri.UserInfo)
            || uri.AbsolutePath != "/"
            || !string.IsNullOrEmpty(uri.Query)
            || !string.IsNullOrEmpty(uri.Fragment))
        {
            throw new InvalidOperationException("App:PublicBaseUrl must be an absolute HTTP(S) origin without credentials, a path, query, or fragment.");
        }

        if (production && uri.Scheme != Uri.UriSchemeHttps)
            throw new InvalidOperationException("App:PublicBaseUrl must use HTTPS in Production.");

        return new ExternalOrigin(uri);
    }

    public string BuildPath(string pathAndQuery)
    {
        if (string.IsNullOrEmpty(pathAndQuery) || pathAndQuery[0] != '/')
            throw new ArgumentException("External paths must start with '/'.", nameof(pathAndQuery));

        return $"{BaseUrl}{pathAndQuery}";
    }

    public bool Matches(HostString host)
    {
        if (!string.Equals(host.Host, _uri.IdnHost, StringComparison.OrdinalIgnoreCase)
            && !string.Equals(host.Host, _uri.Host, StringComparison.OrdinalIgnoreCase))
            return false;

        return _uri.IsDefaultPort
            ? host.Port is null || host.Port == _uri.Port
            : host.Port == _uri.Port;
    }

    public bool AllowsRequest(HostString host, PathString path) =>
        Matches(host) || (path == "/health" && IsInternalHealthHost(host));

    public static bool IsInternalHealthHost(HostString host) => InternalHealthHosts.Contains(host.Host);
}
