using JobsMedical.Web.Models;
using Microsoft.EntityFrameworkCore;

namespace JobsMedical.Web.Data;

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }

    public DbSet<City> Cities => Set<City>();
    public DbSet<District> Districts => Set<District>();
    public DbSet<Role> Roles => Set<Role>();
    public DbSet<User> Users => Set<User>();
    public DbSet<DoctorProfile> DoctorProfiles => Set<DoctorProfile>();
    public DbSet<Facility> Facilities => Set<Facility>();
    public DbSet<Shift> Shifts => Set<Shift>();
    public DbSet<JobOpening> JobOpenings => Set<JobOpening>();
    public DbSet<Application> Applications => Set<Application>();
    public DbSet<RawListing> RawListings => Set<RawListing>();
    public DbSet<Visitor> Visitors => Set<Visitor>();
    public DbSet<UserPreferences> UserPreferences => Set<UserPreferences>();
    public DbSet<InterestEvent> InterestEvents => Set<InterestEvent>();
    public DbSet<AppSetting> AppSettings => Set<AppSetting>();

    protected override void OnModelCreating(ModelBuilder b)
    {
        base.OnModelCreating(b);

        // Phone is the unique identity.
        b.Entity<User>().HasIndex(u => u.Phone).IsUnique();

        // One-to-one User <-> DoctorProfile.
        b.Entity<DoctorProfile>()
            .HasOne(d => d.User)
            .WithOne(u => u.DoctorProfile)
            .HasForeignKey<DoctorProfile>(d => d.UserId)
            .OnDelete(DeleteBehavior.Cascade);

        // A doctor can apply to a shift only once.
        b.Entity<Application>()
            .HasIndex(a => new { a.ShiftId, a.DoctorId })
            .IsUnique();

        b.Entity<Application>()
            .HasOne(a => a.Doctor)
            .WithMany(u => u.Applications)
            .HasForeignKey(a => a.DoctorId)
            .OnDelete(DeleteBehavior.Cascade);

        b.Entity<Application>()
            .HasOne(a => a.Shift)
            .WithMany(s => s.Applications)
            .HasForeignKey(a => a.ShiftId)
            .OnDelete(DeleteBehavior.Cascade);

        // Facility owner is optional (Phase 2 self-serve); don't cascade-delete users.
        b.Entity<Facility>()
            .HasOne(f => f.OwnerUser)
            .WithMany()
            .HasForeignKey(f => f.OwnerUserId)
            .OnDelete(DeleteBehavior.SetNull);

        // Common query indexes: filtering shifts by city/date/status happens constantly.
        b.Entity<Shift>().HasIndex(s => new { s.Date, s.Status });
        b.Entity<Shift>().HasIndex(s => s.FacilityId);
        b.Entity<Shift>().HasIndex(s => s.RoleId);
        b.Entity<Facility>().HasIndex(f => f.CityId);
        b.Entity<Facility>().HasIndex(f => f.DistrictId);

        b.Entity<District>()
            .HasOne(d => d.City).WithMany()
            .HasForeignKey(d => d.CityId).OnDelete(DeleteBehavior.Cascade);
        b.Entity<Facility>()
            .HasOne(f => f.District).WithMany(d => d.Facilities)
            .HasForeignKey(f => f.DistrictId).OnDelete(DeleteBehavior.SetNull);

        // Don't delete shifts/profiles just because a Role is removed.
        b.Entity<Shift>()
            .HasOne(s => s.Role).WithMany(r => r.Shifts)
            .HasForeignKey(s => s.RoleId).OnDelete(DeleteBehavior.Restrict);

        // Visitor identity + behavioral tracking.
        b.Entity<Visitor>()
            .HasOne(v => v.User).WithMany()
            .HasForeignKey(v => v.UserId).OnDelete(DeleteBehavior.SetNull);

        b.Entity<UserPreferences>()
            .HasOne(p => p.Visitor).WithOne(v => v.Preferences)
            .HasForeignKey<UserPreferences>(p => p.VisitorId).OnDelete(DeleteBehavior.Cascade);

        b.Entity<InterestEvent>()
            .HasOne(e => e.Visitor).WithMany(v => v.Events)
            .HasForeignKey(e => e.VisitorId).OnDelete(DeleteBehavior.Cascade);
        b.Entity<InterestEvent>()
            .HasOne(e => e.Shift).WithMany()
            .HasForeignKey(e => e.ShiftId).OnDelete(DeleteBehavior.Cascade);
        b.Entity<InterestEvent>()
            .HasOne(e => e.JobOpening).WithMany()
            .HasForeignKey(e => e.JobOpeningId).OnDelete(DeleteBehavior.Cascade);
        // Reading "this visitor's recent events" is the hot path for recommendations.
        b.Entity<InterestEvent>().HasIndex(e => new { e.VisitorId, e.CreatedAt });

        // Job openings: same query patterns as shifts.
        b.Entity<JobOpening>()
            .HasOne(j => j.Role).WithMany()
            .HasForeignKey(j => j.RoleId).OnDelete(DeleteBehavior.Restrict);
        b.Entity<JobOpening>()
            .HasOne(j => j.Facility).WithMany()
            .HasForeignKey(j => j.FacilityId).OnDelete(DeleteBehavior.Cascade);
        b.Entity<JobOpening>().HasIndex(j => j.Status);
        b.Entity<JobOpening>().HasIndex(j => j.FacilityId);

        // Dedupe ingested listings by content hash.
        b.Entity<RawListing>().HasIndex(r => r.ContentHash);
        b.Entity<RawListing>().HasIndex(r => r.Status);
    }
}
