namespace NatsNet.PortTracker.Audit;

/// <summary>
/// Classifies features by inspecting the SourceIndexer for their .NET implementation status.
/// Priority: n_a lookup -> method-not-found -> stub detection -> verified.
/// </summary>
public sealed class FeatureClassifier
{
    public record ClassificationResult(string Status, string Reason);

    public record FeatureRecord(
        long Id,
        string DotnetClass,
        string DotnetMethod,
        string GoFile,
        string GoMethod);

    private readonly SourceIndexer _indexer;

    // N/A lookup: (goMethod pattern) -> reason
    // Checked case-insensitively against go_method
    private static readonly Dictionary<string, string> NaByGoMethod = new(StringComparer.OrdinalIgnoreCase)
    {
        ["Noticef"] = ".NET uses Microsoft.Extensions.Logging",
        ["Debugf"] = ".NET uses Microsoft.Extensions.Logging",
        ["Tracef"] = ".NET uses Microsoft.Extensions.Logging",
        ["Warnf"] = ".NET uses Microsoft.Extensions.Logging",
        ["Errorf"] = ".NET uses Microsoft.Extensions.Logging",
        ["Fatalf"] = ".NET uses Microsoft.Extensions.Logging",
    };

    // N/A lookup: go_file + go_method patterns
    private static readonly List<(Func<FeatureRecord, bool> Match, string Reason)> NaPatterns =
    [
        // Signal handling — .NET uses IHostApplicationLifetime
        (f => f.GoMethod.Equals("handleSignals", StringComparison.OrdinalIgnoreCase), ".NET uses IHostApplicationLifetime"),
        (f => f.GoMethod.Equals("processSignal", StringComparison.OrdinalIgnoreCase), ".NET uses IHostApplicationLifetime"),
    ];

    public FeatureClassifier(SourceIndexer indexer)
    {
        _indexer = indexer;
    }

    /// <summary>
    /// Classify a single feature. Returns status and reason.
    /// </summary>
    public ClassificationResult Classify(FeatureRecord feature)
    {
        // 1. N/A lookup — check go_method against known patterns
        if (NaByGoMethod.TryGetValue(feature.GoMethod, out var naReason))
            return new ClassificationResult("n_a", naReason);

        foreach (var (match, reason) in NaPatterns)
        {
            if (match(feature))
                return new ClassificationResult("n_a", reason);
        }

        // 2. Handle comma-separated dotnet_class (e.g. "ClosedRingBuffer,ClosedClient")
        var classNames = feature.DotnetClass.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
        var methodName = feature.DotnetMethod;

        // Try each class name
        foreach (var className in classNames)
        {
            var methods = _indexer.Lookup(className, methodName);
            if (methods.Count > 0)
            {
                // Found the method — classify based on body analysis
                // Use the "best" match: prefer non-stub over stub
                var best = methods.OrderByDescending(m => m.StatementCount).First();

                if (best.IsStub)
                    return new ClassificationResult("stub", $"Body is throw NotImplementedException at {Path.GetFileName(best.FilePath)}:{best.LineNumber}");

                if (best.IsPartial)
                    return new ClassificationResult("stub", $"Partial implementation with NotImplementedException at {Path.GetFileName(best.FilePath)}:{best.LineNumber}");

                return new ClassificationResult("verified", $"Method found with {best.StatementCount} statement(s) at {Path.GetFileName(best.FilePath)}:{best.LineNumber}");
            }
        }

        // 3. Method not found — check if any class exists
        var anyClassFound = classNames.Any(c => _indexer.HasClass(c));
        if (anyClassFound)
            return new ClassificationResult("deferred", "Class exists but method not found");

        return new ClassificationResult("deferred", "Class not found in .NET source");
    }
}
