using Baya.Domain.Entities.Catalog;
using Baya.Domain.Entities.Identity;
using Baya.Domain.Entities.Search;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

namespace Baya.Infrastructure.Persistence.Configuration.SearchConfig;

internal sealed class NurseSearchIndexConfig : IEntityTypeConfiguration<NurseSearchIndex>
{
    public void Configure(EntityTypeBuilder<NurseSearchIndex> builder)
    {
        builder.ToTable("NurseSearchIndex", "search");

        // Price is IRR Rials as BIGINT (long → bigint) — copied from the variant. No float money path.
        builder.Property(x => x.PriceUnit).HasMaxLength(20).IsRequired();
        builder.Property(x => x.NurseGender).HasMaxLength(10);
        builder.Property(x => x.NurseName).HasMaxLength(200);
        builder.Property(x => x.AvatarUrl).HasMaxLength(2048);
        builder.Property(x => x.AverageRating).HasPrecision(3, 2);

        // The hot search path: filter on (is_searchable, category, city, district) then rating-sort + page.
        // INCLUDE the columns the projection reads so the filtered, sorted page is served straight from the
        // index with no key lookups (SQL Server; the INCLUDE annotation is ignored by other providers).
        builder.HasIndex(x => new { x.IsSearchable, x.ServiceCategoryId, x.CityId, x.DistrictId })
            .IncludeProperties(x => new { x.Price, x.NurseGender, x.AverageRating, x.TotalReviews, x.NurseId, x.VariantId, x.NurseName })
            .HasDatabaseName("IX_NurseSearchIndex_Search");

        // Exactly one live row per (variant × area) — the upsert target and anti-duplication backstop.
        // SQL Server treats NULLs as distinct, so a plain UNIQUE(variant, city, district) would wrongly allow
        // two "whole city" (NULL district) rows. Split into a filtered pair exactly like nurse_service_areas:
        // one enforces at most one whole-city row, the other enforces uniqueness of city+district rows. Both
        // exclude soft-deleted rows so a removed-then-recovered area re-inserts cleanly.
        builder.HasIndex(x => new { x.VariantId, x.CityId })
            .IsUnique()
            .HasFilter("[DistrictId] IS NULL AND [DeletedAt] IS NULL")
            .HasDatabaseName("UX_NurseSearchIndex_Variant_City_WholeCity");

        builder.HasIndex(x => new { x.VariantId, x.CityId, x.DistrictId })
            .IsUnique()
            .HasFilter("[DistrictId] IS NOT NULL AND [DeletedAt] IS NULL")
            .HasDatabaseName("UX_NurseSearchIndex_Variant_City_District");

        // A nurse-scoped rebuild / suspend / remove touches every row for one nurse — keep it cheap.
        builder.HasIndex(x => x.NurseId);

        builder.HasOne(x => x.Variant)
            .WithMany()
            .HasForeignKey(x => x.VariantId)
            .IsRequired();

        builder.HasOne<NurseProfile>()
            .WithMany()
            .HasForeignKey(x => x.NurseId)
            .IsRequired();

        builder.HasQueryFilter(x => x.DeletedAt == null);
    }
}
