using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage;
using TeleWave.Application.Common.Interfaces;
using TeleWave.Domain.Auth;
using TeleWave.Domain.Broadcast;
using TeleWave.Domain.Images;
using TeleWave.Domain.Library;
using TeleWave.Domain.Media;
using TeleWave.Domain.Programming;
using TeleWave.Domain.Settings;
using TeleWave.Infrastructure.Identity;

namespace TeleWave.Infrastructure.Persistence;

/// <summary>
/// Корневой DbContext приложения: Identity-схема (пользователи/роли) + сущности домена
/// (добавляются по мере реализации фич).
/// </summary>
public class AppDbContext(DbContextOptions<AppDbContext> options)
    : IdentityDbContext<AppUser, AppRole, Guid>(options),
        IAppDbContext
{
    public DbSet<RefreshToken> RefreshTokens => Set<RefreshToken>();
    public DbSet<MediaAsset> MediaAssets => Set<MediaAsset>();
    public DbSet<Show> Shows => Set<Show>();
    public DbSet<Genre> Genres => Set<Genre>();
    public DbSet<GenreAlias> GenreAliases => Set<GenreAlias>();
    public DbSet<ShowGenre> ShowGenres => Set<ShowGenre>();
    public DbSet<Collection> Collections => Set<Collection>();
    public DbSet<CollectionItem> CollectionItems => Set<CollectionItem>();
    public DbSet<Group> Groups => Set<Group>();
    public DbSet<GroupItem> GroupItems => Set<GroupItem>();
    public DbSet<ScheduleTemplate> ScheduleTemplates => Set<ScheduleTemplate>();
    public DbSet<GridLayer> GridLayers => Set<GridLayer>();
    public DbSet<Slot> Slots => Set<Slot>();
    public DbSet<SlotState> SlotStates => Set<SlotState>();
    public DbSet<JunctionTemplate> JunctionTemplates => Set<JunctionTemplate>();
    public DbSet<JunctionElement> JunctionElements => Set<JunctionElement>();
    public DbSet<Channel> Channels => Set<Channel>();
    public DbSet<ScheduleEntry> ScheduleEntries => Set<ScheduleEntry>();
    public DbSet<BumperTemplate> BumperTemplates => Set<BumperTemplate>();
    public DbSet<BumperTextVariant> BumperTextVariants => Set<BumperTextVariant>();
    public DbSet<BumperAsset> BumperAssets => Set<BumperAsset>();
    public DbSet<AppSetting> AppSettings => Set<AppSetting>();
    public DbSet<Image> Images => Set<Image>();

    public Task<IDbContextTransaction> BeginTransactionAsync(CancellationToken cancellationToken) =>
        Database.BeginTransactionAsync(cancellationToken);

    public Task AcquireChannelLockAsync(Guid channelId, CancellationToken cancellationToken)
    {
        // pg_advisory_xact_lock(bigint) освобождается автоматически при завершении транзакции.
        // Ключ — стабильный int64 из GUID канала; коллизии между каналами лишь сериализуют их генерацию,
        // корректности не нарушают.
        var key = BitConverter.ToInt64(channelId.ToByteArray());
        return Database.ExecuteSqlAsync($"SELECT pg_advisory_xact_lock({key})", cancellationToken);
    }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
        builder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly);

        // Guid-ключи доменных сущностей мы задаём сами в фабриках. Без этого EF считает выставленный
        // ключ признаком уже существующей строки и при добавлении дочерней сущности через коллекцию
        // отслеживаемого родителя (напр. show.AddEpisode) делает UPDATE вместо INSERT → «affected 0».
        foreach (var entityType in builder.Model.GetEntityTypes())
        {
            if (
                entityType.ClrType.Namespace?.StartsWith(
                    "TeleWave.Domain",
                    StringComparison.Ordinal
                ) != true
            )
                continue;

            var idProperty = entityType.FindProperty("Id");
            if (idProperty is not null && idProperty.ClrType == typeof(Guid))
                idProperty.ValueGenerated = ValueGenerated.Never;
        }
    }
}
