using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Audit;
using ZB.MOM.WW.ScadaBridge.Commons.Types;

namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Configurations;

/// <summary>
/// Maps the <see cref="SiteCall"/> record to the central <c>SiteCalls</c> table.
/// Operational state — NOT audit —
/// so the table is non-partitioned, standard <c>[PRIMARY]</c> filegroup, no DB-role
/// restriction. Two named indexes back the Central UI's "from this site" and
/// "in this status" queries.
/// </summary>
public class SiteCallEntityTypeConfiguration : IEntityTypeConfiguration<SiteCall>
{
    /// <summary>
    /// Configures the EF Core entity type mapping for <see cref="SiteCall"/>.
    /// </summary>
    /// <param name="builder">The entity type builder to configure.</param>
    public void Configure(EntityTypeBuilder<SiteCall> builder)
    {
        builder.ToTable("SiteCalls");

        // PK is the strong-typed TrackedOperationId. Stored as varchar(36) by converting
        // through the canonical "D"-format GUID string. Going through the string surface
        // (rather than uniqueidentifier) keeps the column shape identical to how the id
        // is serialised on the wire (gRPC strings, SQLite TEXT on the site) — one
        // consistent format everywhere makes operational debugging far easier than
        // mixing a uniqueidentifier central column with TEXT site columns.
        builder.HasKey(s => s.TrackedOperationId);

        builder.Property(s => s.TrackedOperationId)
            .HasConversion(
                id => id.Value.ToString("D"),
                s => new TrackedOperationId(Guid.Parse(s)))
            .HasMaxLength(36)
            .IsUnicode(false)
            .IsRequired();

        // Enum-as-string columns: bounded varchar, ASCII.
        builder.Property(s => s.Channel)
            .HasMaxLength(32)
            .IsUnicode(false)
            .IsRequired();

        builder.Property(s => s.Status)
            .HasMaxLength(32)
            .IsUnicode(false)
            .IsRequired();

        builder.Property(s => s.SourceSite)
            .HasMaxLength(64)
            .IsUnicode(false)
            .IsRequired();

        builder.Property(s => s.Target)
            .HasMaxLength(256)
            .IsUnicode(false)
            .IsRequired();

        // Bounded unicode message column.
        builder.Property(s => s.LastError)
            .HasMaxLength(1024);

        // SourceNode (SourceNode-stamping): node-local identifier of the
        // cluster member that produced the call (e.g. "node-a", "central-a"). NULL is
        // valid for rows that pre-date this feature and for reconciled rows from a
        // retired node. ASCII — varchar(64). Not a key column of any index, but it rides
        // as an INCLUDE column on IX_SiteCalls_NonTerminal below so the per-node KPI
        // breakdown reads it covered off the filtered live-queue index.
        builder.Property(s => s.SourceNode)
            .HasColumnType("varchar(64)")
            .HasMaxLength(64)
            .IsUnicode(false);

        // Indexes — names locked for reconciliation/migration discoverability.
        // Source_Created backs "calls from this site" (Central UI Site Calls page,
        // filter by SourceSite, newest first).
        builder.HasIndex(s => new { s.SourceSite, s.CreatedAtUtc })
            .IsDescending(false, true)
            .HasDatabaseName("IX_SiteCalls_Source_Created");

        // Status_Updated backs "calls in this status" (e.g. parked rows awaiting
        // operator action, newest UpdatedAtUtc first).
        builder.HasIndex(s => new { s.Status, s.UpdatedAtUtc })
            .IsDescending(false, true)
            .HasDatabaseName("IX_SiteCalls_Status_Updated");

        // NonTerminal backs every "live queue" KPI predicate (TerminalAtUtc IS NULL):
        // buffered/stuck/oldest counts run every 60 s from the KPI recorder plus every
        // 10 s Health-dashboard poll. Filtered: the non-terminal population is the live
        // queue, not the 365-day archive — each count becomes a small seek instead of a
        // clustered scan (arch-review 04, P1). SourceSite/SourceNode/Status ride as
        // INCLUDE columns so the per-site + per-node KPI breakdowns stay covered.
        builder.HasIndex(s => s.CreatedAtUtc)
            .HasDatabaseName("IX_SiteCalls_NonTerminal")
            .HasFilter("[TerminalAtUtc] IS NULL")
            .IncludeProperties(nameof(SiteCall.SourceSite), nameof(SiteCall.SourceNode), nameof(SiteCall.Status));

        // Terminal backs the daily retention purge's predicate (TerminalAtUtc IS NOT NULL
        // AND TerminalAtUtc < cutoff) and Task 11's MIN() slice anchor: filtered to the
        // terminal population so the purge seeks the expired tail instead of full-scanning
        // the 365-day table (arch-review 04 round 2, R6). Complements IX_SiteCalls_NonTerminal,
        // which is filtered to IS NULL and unusable for this predicate.
        builder.HasIndex(s => s.TerminalAtUtc)
            .HasDatabaseName("IX_SiteCalls_Terminal")
            .HasFilter("[TerminalAtUtc] IS NOT NULL");
    }
}
