using MongoDB.Driver;

namespace Nalu.Jobs;

public class NameRepository
{
    private readonly IMongoCollection<NomeBr> _nomes;
    private readonly IMongoCollection<JobRun> _jobRuns;

    public NameRepository(IMongoDatabase db)
    {
        _nomes   = db.GetCollection<NomeBr>("nomes_br");
        _jobRuns = db.GetCollection<JobRun>("job_runs");
    }

    public async Task EnsureIndexesAsync(CancellationToken ct)
    {
        var model = new CreateIndexModel<NomeBr>(
            Builders<NomeBr>.IndexKeys.Ascending(x => x.Nome),
            new CreateIndexOptions { Unique = true, Name = "idx_nome_unique" });

        await _nomes.Indexes.CreateOneAsync(model, cancellationToken: ct);
    }

    /// <summary>
    /// Upserts a single name.
    /// New doc: inserts all fields. Existing: increments frequencia, merges genero, updates timestamp.
    /// Returns true if inserted (new), false if updated.
    /// </summary>
    public async Task<bool> UpsertAsync(AggregatedName name, DateTime now, CancellationToken ct)
    {
        var nomeDisplay = ToTitleCase(name.Nome);
        var filter      = Builders<NomeBr>.Filter.Eq(x => x.Nome, name.Nome);
        var existing    = await _nomes.Find(filter).FirstOrDefaultAsync(ct);

        UpdateDefinition<NomeBr> update;

        if (existing is null)
        {
            update = Builders<NomeBr>.Update
                .SetOnInsert(x => x.Nome,        name.Nome)
                .SetOnInsert(x => x.NomeDisplay, nomeDisplay)
                .SetOnInsert(x => x.Tipo,        "primeiro_nome")
                .SetOnInsert(x => x.Genero,      name.Genero)
                .SetOnInsert(x => x.Fonte,       "ibge_censo")
                .Inc(x => x.Frequencia,          name.Frequencia)
                .Set(x => x.UltimaAtualizacao,   now);
        }
        else
        {
            var mergedGenero = existing.Genero == name.Genero ? existing.Genero : "N";
            update = Builders<NomeBr>.Update
                .Inc(x => x.Frequencia,        name.Frequencia)
                .Set(x => x.Genero,            mergedGenero)
                .Set(x => x.UltimaAtualizacao, now);
        }

        var result = await _nomes.UpdateOneAsync(filter, update,
            new UpdateOptions { IsUpsert = true }, ct);

        return result.UpsertedId is not null;
    }

    public async Task SaveJobRunAsync(JobRun run, CancellationToken ct) =>
        await _jobRuns.InsertOneAsync(run, null, ct);

    private static string ToTitleCase(string upper) =>
        string.Join(" ", upper.Split(' ')
            .Select(w => w.Length == 0 ? w :
                char.ToUpperInvariant(w[0]) + w[1..].ToLowerInvariant()));
}
