using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using RealCV.Domain.Entities;
using RealCV.Infrastructure.Identity;

namespace RealCV.Infrastructure.Data;

public class ApplicationDbContext : IdentityDbContext<ApplicationUser, IdentityRole<Guid>, Guid>
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }

    public DbSet<CVCheck> CVChecks => Set<CVCheck>();
    public DbSet<CVFlag> CVFlags => Set<CVFlag>();
    public DbSet<CompanyCache> CompanyCache => Set<CompanyCache>();
    public DbSet<AuditLog> AuditLogs => Set<AuditLog>();

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

        ConfigureApplicationUser(builder);
        ConfigureCVCheck(builder);
        ConfigureCVFlag(builder);
        ConfigureCompanyCache(builder);
        ConfigureAuditLog(builder);
    }

    private static void ConfigureApplicationUser(ModelBuilder builder)
    {
        builder.Entity<ApplicationUser>(entity =>
        {
            entity.Property(u => u.Plan)
                .HasConversion<string>()
                .HasMaxLength(32);

            entity.Property(u => u.StripeCustomerId)
                .HasMaxLength(256);

            entity.Property(u => u.StripeSubscriptionId)
                .HasMaxLength(256);

            entity.Property(u => u.SubscriptionStatus)
                .HasMaxLength(32);

            entity.HasIndex(u => u.StripeCustomerId)
                .HasDatabaseName("IX_Users_StripeCustomerId");

            entity.HasIndex(u => u.StripeSubscriptionId)
                .HasDatabaseName("IX_Users_StripeSubscriptionId");

            entity.HasMany(u => u.CVChecks)
                .WithOne()
                .HasForeignKey(c => c.UserId)
                .OnDelete(DeleteBehavior.Cascade);
        });
    }

    private static void ConfigureCVCheck(ModelBuilder builder)
    {
        builder.Entity<CVCheck>(entity =>
        {
            entity.Property(c => c.Status)
                .HasConversion<string>()
                .HasMaxLength(32);

            entity.HasIndex(c => c.UserId)
                .HasDatabaseName("IX_CVChecks_UserId");

            entity.HasIndex(c => c.Status)
                .HasDatabaseName("IX_CVChecks_Status");

            entity.HasMany(c => c.Flags)
                .WithOne(f => f.CVCheck)
                .HasForeignKey(f => f.CVCheckId)
                .OnDelete(DeleteBehavior.Cascade);
        });
    }

    private static void ConfigureCVFlag(ModelBuilder builder)
    {
        builder.Entity<CVFlag>(entity =>
        {
            entity.Property(f => f.Category)
                .HasConversion<string>()
                .HasMaxLength(32);

            entity.Property(f => f.Severity)
                .HasConversion<string>()
                .HasMaxLength(32);

            entity.HasIndex(f => f.CVCheckId)
                .HasDatabaseName("IX_CVFlags_CVCheckId");
        });
    }

    private static void ConfigureCompanyCache(ModelBuilder builder)
    {
        builder.Entity<CompanyCache>(entity =>
        {
            entity.HasKey(c => c.CompanyNumber);

            entity.Property(c => c.CompanyNumber)
                .HasMaxLength(32);
        });
    }

    private static void ConfigureAuditLog(ModelBuilder builder)
    {
        builder.Entity<AuditLog>(entity =>
        {
            entity.HasIndex(a => a.UserId)
                .HasDatabaseName("IX_AuditLogs_UserId");

            entity.HasIndex(a => a.CreatedAt)
                .HasDatabaseName("IX_AuditLogs_CreatedAt");

            entity.HasIndex(a => a.Action)
                .HasDatabaseName("IX_AuditLogs_Action");
        });
    }

    public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
    {
        var newCVChecks = ChangeTracker.Entries<CVCheck>()
            .Where(e => e.State == EntityState.Added)
            .Select(e => e.Entity);

        foreach (var cvCheck in newCVChecks)
        {
            cvCheck.CreatedAt = DateTime.UtcNow;
        }

        return await base.SaveChangesAsync(cancellationToken);
    }
}
