using System.Text.Json;
using BigRegister.Api.Contracts;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;

namespace BigRegister.Api.Data;

/// <summary>
/// EF Core/SQLite persistence for the three stores that used to be static
/// in-memory dictionaries (WP-22): <see cref="Aanvraag"/>, <see cref="StoredDocument"/>
/// + <see cref="AuditEntry"/>, and <see cref="BriefEntity"/>. Opaque nested shapes
/// (a wizard's draft snapshot, a brief's sections/placeholders/status) are stored as
/// JSON text columns rather than redesigned into relational tables — the backend
/// already treats them as opaque (see BriefStore's own header comment), so a JSON
/// column matches that "don't interpret it" posture with zero schema redesign,
/// per this WP's own decision to relocate shapes, not redesign them.
/// </summary>
public sealed class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(options)
{
  public DbSet<StoredDocument> Documents => Set<StoredDocument>();
  public DbSet<AuditEntry> AuditEntries => Set<AuditEntry>();
  public DbSet<Aanvraag> Applications => Set<Aanvraag>();
  public DbSet<BriefEntity> Briefs => Set<BriefEntity>();
  public DbSet<OrgTemplateEntity> OrgTemplates => Set<OrgTemplateEntity>();

  protected override void OnModelCreating(ModelBuilder modelBuilder)
  {
    modelBuilder.Entity<StoredDocument>().HasKey(d => d.DocumentId);

    modelBuilder.Entity<AuditEntry>(e =>
    {
      e.HasKey(a => a.Id);
      e.Property(a => a.Id).ValueGeneratedOnAdd();
    });

    modelBuilder.Entity<Aanvraag>(e =>
    {
      e.HasKey(a => a.Id);
      e.Property(a => a.Draft).HasConversion(DraftConverter);
      e.Property(a => a.DocumentIds).HasConversion(Json<List<string>>());
    });

    modelBuilder.Entity<BriefEntity>(e =>
    {
      e.HasKey(b => b.BriefId);
      e.HasIndex(b => b.Owner).IsUnique(); // one demo brief per owner (GetOrCreate's invariant)
      e.Property(b => b.Placeholders).HasConversion(Json<IReadOnlyList<PlaceholderDefDto>>());
      e.Property(b => b.Sections).HasConversion(Json<List<LetterSectionDto>>());
      e.Property(b => b.Status).HasConversion(Json<BriefStatusDto>());
    });

    modelBuilder.Entity<OrgTemplateEntity>(e =>
    {
      e.HasKey(t => t.SubOrgId);
      e.Property(t => t.Draft).HasConversion(Json<OrgTemplateDto>());
      e.Property(t => t.History).HasConversion(Json<List<OrgTemplateVersionDto>>());
    });
  }

  // A wizard's draft is an opaque JsonElement snapshot — stored as its raw JSON
  // text. `.Clone()` on read detaches the element from the short-lived JsonDocument
  // that parsed it (same rule ApplicationStore.SyncDraft already follows for the
  // request body — an uncloned element is invalid once its JsonDocument is GC'd).
  private static readonly ValueConverter<JsonElement?, string?> DraftConverter = new(
      v => v.HasValue ? v.Value.GetRawText() : null,
      v => string.IsNullOrEmpty(v) ? (JsonElement?)null : JsonDocument.Parse(v).RootElement.Clone());

  private static ValueConverter<T, string> Json<T>() => new(
      v => JsonSerializer.Serialize(v, (JsonSerializerOptions?)null),
      v => JsonSerializer.Deserialize<T>(v, (JsonSerializerOptions?)null)!);
}
