namespace NatsNet.PortTracker.Audit;

using NatsNet.PortTracker.Data;

/// <summary>
/// Verifies status updates against audit classification results.
/// Used by feature and test update commands to ensure status accuracy.
/// </summary>
public static class AuditVerifier
{
    public record VerificationResult(
        long ItemId,
        string AuditStatus,
        string AuditReason,
        bool Matches);

    private static readonly Dictionary<string, string> DefaultSourcePaths = new()
    {
        ["features"] = Path.Combine(Directory.GetCurrentDirectory(), "dotnet", "src", "ZB.MOM.NatsNet.Server"),
        ["unit_tests"] = Path.Combine(Directory.GetCurrentDirectory(), "dotnet", "tests", "ZB.MOM.NatsNet.Server.Tests")
    };

    /// <summary>
    /// Build a SourceIndexer for the appropriate table type.
    /// </summary>
    public static SourceIndexer BuildIndexer(string tableName)
    {
        var sourcePath = DefaultSourcePaths[tableName];
        if (!Directory.Exists(sourcePath))
            throw new DirectoryNotFoundException($"Source directory not found: {sourcePath}");

        Console.WriteLine($"Building audit index from {sourcePath}...");
        var indexer = new SourceIndexer();
        indexer.IndexDirectory(sourcePath);
        Console.WriteLine($"Indexed {indexer.FilesIndexed} files, {indexer.MethodsIndexed} methods/properties.");
        return indexer;
    }

    /// <summary>
    /// Verify items matching a WHERE clause against audit classification.
    /// </summary>
    public static List<VerificationResult> VerifyItems(
        Database db, SourceIndexer indexer, string tableName,
        string whereClause, List<(string, object?)> parameters, string requestedStatus)
    {
        var sql = $"SELECT id, dotnet_class, dotnet_method, go_file, go_method FROM {tableName}{whereClause} ORDER BY id";
        var rows = db.Query(sql, parameters.ToArray());

        var classifier = new FeatureClassifier(indexer);
        var results = new List<VerificationResult>();

        foreach (var row in rows)
        {
            var record = new FeatureClassifier.FeatureRecord(
                Id: Convert.ToInt64(row["id"]),
                DotnetClass: row["dotnet_class"]?.ToString() ?? "",
                DotnetMethod: row["dotnet_method"]?.ToString() ?? "",
                GoFile: row["go_file"]?.ToString() ?? "",
                GoMethod: row["go_method"]?.ToString() ?? "");

            var classification = classifier.Classify(record);
            var matches = classification.Status == requestedStatus;
            results.Add(new VerificationResult(record.Id, classification.Status, classification.Reason, matches));
        }

        return results;
    }

    /// <summary>
    /// Check verification results and print a report.
    /// Returns true if the update should proceed.
    /// </summary>
    public static bool CheckAndReport(
        List<VerificationResult> results, string requestedStatus, string? overrideComment)
    {
        var matches = results.Where(r => r.Matches).ToList();
        var mismatches = results.Where(r => !r.Matches).ToList();

        Console.WriteLine($"\nAudit verification: {matches.Count} match, {mismatches.Count} mismatch");

        if (mismatches.Count == 0)
            return true;

        Console.WriteLine($"\nMismatches (requested '{requestedStatus}'):");
        foreach (var m in mismatches.Take(20))
            Console.WriteLine($"  ID {m.ItemId}: audit says '{m.AuditStatus}' ({m.AuditReason})");
        if (mismatches.Count > 20)
            Console.WriteLine($"  ... and {mismatches.Count - 20} more");

        if (overrideComment is null)
        {
            Console.WriteLine($"\n{mismatches.Count} items have audit mismatches. Use --override \"reason\" to force.");
            return false;
        }

        Console.WriteLine($"\nOverride applied: \"{overrideComment}\"");
        return true;
    }

    /// <summary>
    /// Log override records to the status_overrides table.
    /// </summary>
    public static void LogOverrides(
        Database db, string tableName, IEnumerable<VerificationResult> mismatches,
        string requestedStatus, string comment)
    {
        var mismatchList = mismatches.ToList();
        if (mismatchList.Count == 0) return;

        using var transaction = db.Connection.BeginTransaction();
        try
        {
            foreach (var mismatch in mismatchList)
            {
                using var cmd = db.CreateCommand(
                    "INSERT INTO status_overrides (table_name, item_id, audit_status, audit_reason, requested_status, comment) " +
                    "VALUES (@table, @item, @auditStatus, @auditReason, @requestedStatus, @comment)");
                cmd.Parameters.AddWithValue("@table", tableName);
                cmd.Parameters.AddWithValue("@item", mismatch.ItemId);
                cmd.Parameters.AddWithValue("@auditStatus", mismatch.AuditStatus);
                cmd.Parameters.AddWithValue("@auditReason", mismatch.AuditReason);
                cmd.Parameters.AddWithValue("@requestedStatus", requestedStatus);
                cmd.Parameters.AddWithValue("@comment", comment);
                cmd.Transaction = transaction;
                cmd.ExecuteNonQuery();
            }
            transaction.Commit();
            Console.WriteLine($"Logged {mismatchList.Count} override(s) to status_overrides table.");
        }
        catch
        {
            transaction.Rollback();
            throw;
        }
    }
}
