#nullable enable
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Baya.Application.Contracts.Audit;
using Baya.Application.Contracts.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace Baya.Infrastructure.Persistence.Services.Scheduling.Jobs;

/// <summary>
/// Retention/archival policy for the append-only <c>ops.AuditLogs</c> table (refinement-phase-9 §9.4). The audit
/// trail is never pruned by the application except here, on a schedule, and even then a two-tier policy protects
/// the legally-sensitive rows: <b>financial &amp; verification</b> entity types (refunds, clawbacks, payouts,
/// payout batches, verification decisions, config changes, partner centers) keep a long window
/// (<c>audit_retention_financial_days</c>, default ~7 years); everyday operational rows keep a shorter one
/// (<c>audit_retention_general_days</c>, default ~2 years). Idempotent: a re-run simply finds nothing new to
/// delete. Runs on the <c>audit_retention_scan_cadence_hours</c> cadence.
/// </summary>
internal sealed class AuditLogRetentionJob(ILogger<AuditLogRetentionJob> logger) : IRecurringJob
{
    // Bound the per-run working set so a large backlog drains across successive runs without loading the whole table.
    private const int MaxRowsPerRun = 20_000;

    /// <summary>
    /// The <c>IAuditable</c> entity types whose audit rows carry money- or verification-legal weight. Their
    /// <c>EntityType</c> is the CLR type name written by <c>AuditFieldInterceptor</c>. Everything else uses the
    /// shorter general window.
    /// </summary>
    private static readonly IReadOnlyCollection<string> FinancialEntityTypes =
    [
        "Refund",
        "NurseClawback",
        "NursePayout",
        "NursePayoutBatch",
        "NurseVerification",
        "PlatformConfig",
        "PartnerCenter"
    ];

    public string Name => "audit_log_retention";

    public async ValueTask<TimeSpan> GetIntervalAsync(IServiceProvider services, CancellationToken cancellationToken)
    {
        var config = services.GetRequiredService<IPlatformConfig>();
        var hours = await config.GetConfig<int>("audit_retention_scan_cadence_hours", cancellationToken);
        return TimeSpan.FromHours(hours);
    }

    public async ValueTask RunAsync(IServiceProvider services, CancellationToken cancellationToken)
    {
        var config = services.GetRequiredService<IPlatformConfig>();
        var audit = services.GetRequiredService<IAuditLogger>();

        var generalDays = await config.GetConfig<int>("audit_retention_general_days", cancellationToken);
        var financialDays = await config.GetConfig<int>("audit_retention_financial_days", cancellationToken);

        var deleted = await audit.PurgeExpiredAsync(
            generalDays, financialDays, FinancialEntityTypes, MaxRowsPerRun, cancellationToken);

        if (deleted > 0)
            logger.LogInformation(
                "Audit retention purged {Count} audit row(s) (general>{GeneralDays}d, financial>{FinancialDays}d)",
                deleted, generalDays, financialDays);
    }
}
