using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using MongoDB.Driver;

namespace Nalu.Jobs;

public class NaluNameImporterJob : INameImporterJob
{
    private readonly IMongoClient _mongoClient;
    private readonly IConfiguration _config;
    private readonly ILogger<NaluNameImporterJob> _logger;

    public NaluNameImporterJob(
        IMongoClient mongoClient,
        IConfiguration config,
        ILogger<NaluNameImporterJob> logger)
    {
        _mongoClient = mongoClient;
        _config      = config;
        _logger      = logger;
    }

    public async Task ExecuteAsync(bool forceFull = false)
    {
        _logger.LogInformation("NaluNameImporter starting (forceFull={ForceFull})", forceFull);

        var db   = ResolveDatabase();
        var repo = new NameRepository(db);

        var startedAt = DateTime.UtcNow;
        int totalProcessed = 0, totalInserted = 0, totalUpdated = 0;
        string? errorMessage = null;

        try
        {
            await repo.EnsureIndexesAsync(CancellationToken.None);

            using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
            http.DefaultRequestHeaders.UserAgent.ParseAdd("NaluNameImporter/1.0");

            var names = await IbgeClient.FetchNamesAsync(http, _logger, CancellationToken.None);

            if (names.Count == 0)
                throw new InvalidOperationException("IBGE returned 0 names.");

            _logger.LogInformation("Starting upsert of {Count} names", names.Count);

            var now = DateTime.UtcNow;

            foreach (var kv in names)
            {
                var wasInserted = await repo.UpsertAsync(kv.Value, now, CancellationToken.None);

                totalProcessed++;
                if (wasInserted) totalInserted++;
                else             totalUpdated++;

                if (totalProcessed % 500 == 0 || totalProcessed == names.Count)
                {
                    _logger.LogInformation(
                        "[{Processed}/{Total}] inserted={Inserted} updated={Updated}",
                        totalProcessed, names.Count, totalInserted, totalUpdated);
                }
            }

            _logger.LogInformation(
                "NaluNameImporter done — processed={P} inserted={I} updated={U}",
                totalProcessed, totalInserted, totalUpdated);

            // ── Step 2: import curated list ────────────────────────────────────
            _logger.LogInformation("Step 2: importing curated names list...");
            await CuratedNamesImporter.ImportAsync(db, _logger, CancellationToken.None);
        }
        catch (Exception ex)
        {
            errorMessage = ex.Message;
            _logger.LogError(ex, "NaluNameImporter failed: {Message}", ex.Message);
            throw; // re-throw so Hangfire marks job as failed and retries
        }
        finally
        {
            try
            {
                await repo.SaveJobRunAsync(new JobRun
                {
                    Job              = "NaluNameImporter",
                    ExecutadoEm      = startedAt,
                    TotalProcessados = totalProcessed,
                    TotalInseridos   = totalInserted,
                    TotalAtualizados = totalUpdated,
                    Status           = errorMessage is null ? "sucesso" : "erro",
                    Erro             = errorMessage
                }, CancellationToken.None);
            }
            catch (Exception logEx)
            {
                _logger.LogWarning(logEx, "Failed to save job_run log");
            }
        }
    }

    private IMongoDatabase ResolveDatabase()
    {
        // Try the ASP.NET Core connection string first, then env-var style
        var connStr = _config.GetConnectionString("MongoDB")
            ?? _config["MONGO_CONNECTION_STRING"]
            ?? "mongodb://localhost:27017";

        // Database name: from MongoUrl path segment, then explicit config, then default
        var mongoUrl = MongoDB.Driver.MongoUrl.Create(connStr);
        var dbName   = (!string.IsNullOrWhiteSpace(mongoUrl.DatabaseName) ? mongoUrl.DatabaseName : null)
            ?? _config["MONGO_DATABASE"]
            ?? "nalu";

        return _mongoClient.GetDatabase(dbName);
    }
}
