using Knot.Contracts.Auth.Application.Abstractions;
using Knot.Contracts.Auth.Domain;
using Knot.Contracts.Auth.Infrastructure.Persistence;
using Knot.Shared.Kernel;
using Knot.Shared.Kernel.Security;
using MediatR;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using DomainUser = Knot.Modules.Auth.Domain.User;

namespace Knot.Modules.Auth.Infrastructure.Persistence;

public sealed class AuthDbContext : DbContext, IAuthUnitOfWork, Knot.Contracts.Auth.Application.Abstractions.IAuthDbContext, Knot.Contracts.Auth.Infrastructure.Persistence.IAuthDbContext
{
    private readonly IMediator _mediator;
    private readonly IEncryptionService _encryptionService;

    public AuthDbContext(DbContextOptions<AuthDbContext> options, IMediator mediator, IEncryptionService encryptionService)
        : base(options)
    {
        _mediator = mediator;
        _encryptionService = encryptionService;
    }

    public DbSet<DomainUser> Users => Set<DomainUser>();

    public IUserRepository UserRepository => throw new InvalidOperationException("UserRepository should be resolved from DI");

    public void Add(UserContract user)
    {
        var domainUser = DomainUser.Create(user.Username, user.PasswordHash, user.DisplayName, user.Email, user.Bio);
        Set<DomainUser>().Add(domainUser);
    }

    IQueryable<UserContract> Knot.Contracts.Auth.Infrastructure.Persistence.IAuthDbContext.Users =>
        Set<DomainUser>().Select(u => new UserContract
        {
            Id = u.Id,
            Username = u.Username,
            DisplayName = u.DisplayName,
            Email = u.Email,
            Avatar = u.Avatar,
            PhoneNumber = u.PhoneNumber,
            CreatedAt = u.CreatedAt,
            IsBanned = u.IsBanned,
            Bio = u.Bio,
            PasswordHash = u.PasswordHash
        });

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        base.OnConfiguring(optionsBuilder);
        optionsBuilder.ConfigureWarnings(w => w.Ignore(RelationalEventId.PendingModelChangesWarning));
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.HasDefaultSchema("identity");

        modelBuilder.Entity<DomainUser>(builder =>
        {
            builder.ToTable("Users");
            builder.HasKey(u => u.Id);
            builder.Property(u => u.Username).IsRequired().HasMaxLength(50);
            builder.HasIndex(u => u.Username).IsUnique();
            builder.Property(u => u.PasswordHash).IsRequired();
        });
    }

    public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
    {
        var domainEvents = ChangeTracker
            .Entries<IAggregateRoot>()
            .SelectMany(x =>
            {
                if (x.Entity is AggregateRoot<Guid> root)
                {
                    var events = root.GetDomainEvents().ToList();
                    root.ClearDomainEvents();
                    return events;
                }
                return Enumerable.Empty<IDomainEvent>();
            })
            .ToList();

        int result = await base.SaveChangesAsync(cancellationToken);

        foreach (var domainEvent in domainEvents)
        {
            await _mediator.Publish(domainEvent, cancellationToken);
        }

        return result;
    }
}

