using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AyCode.Core.Extensions;
using AyCode.Core.Helpers;
using AyCode.Database.DbContexts;
using AyCode.Database.Extensions;
using AyCode.Entities;
using AyCode.Interfaces;
using AyCode.Interfaces.Entities;
using AyCode.Utils.Extensions;
using Microsoft.EntityFrameworkCore;

namespace AyCode.Database.DataLayers;

public abstract class AcDalBase<TDbContext> : IAcDalBase<TDbContext> where TDbContext : AcDbContextBase
{
    public string Name { get; private set; }

    public bool AutoCloseSession { get; set; }

    /// <summary>
    /// Do not use it! Use Session or Transaction... - J.
    /// </summary>
    public TDbContext Context { get; private set; }

    public Mutex MutextLock { get; } = new();
    //protected AcDalBase()
    //{
    //    Name = $"{GetType().Name}";
    //}

    protected AcDalBase() : this(Activator.CreateInstance<TDbContext>())
    { }

    protected AcDalBase(TDbContext ctx)
    {
        Context = ctx;

        if (Context.Name.IsNullOrWhiteSpace())
            Context.Name = Context.GetType().Name;

        Name = $"{GetType().Name}, {Context.Name}";
    }

    public TDbContext CreateDbContext()
    {
        return Activator.CreateInstance<TDbContext>();
    }

    public void CloseDbContext(ref TDbContext? ctx)
    {
        ctx?.Dispose();
        ctx = null;
    }

    public TModelDto? GetModelDtoById<TModelDto, TEntity>(Guid id) where TModelDto : class, IAcModelDtoBase where TEntity : class, IEntity
        => Session(ctx => ctx.GetModelDtoById<TModelDto, TEntity>(id));

    public Task<TModelDto?> GetModelDtoByIdAsync<TModelDto, TEntity>(Guid id) where TModelDto : class, IAcModelDtoBase where TEntity : class, IEntity
        => SessionAsync(ctx => ctx.GetModelDtoById<TModelDto, TEntity>(id));

    public List<TModelDto> GetAllModelDto<TModelDto, TEntity>() where TModelDto : class, IAcModelDtoBase where TEntity : class, IEntity
        => Session(ctx => ctx.GetAllModelDto<TModelDto, TEntity>().ToList());
    public Task<List<TModelDto>> GetAllModelDtoAsync<TModelDto, TEntity>() where TModelDto : class, IAcModelDtoBase where TEntity : class, IEntity
        => SessionAsync(ctx => ctx.GetAllModelDto<TModelDto, TEntity>().ToList());


    public Task<TResultType> SessionAsync<TResultType>(Func<TDbContext, TResultType> callback)
        => this.SessionAsync<TDbContext, TResultType>(callback);

    public Task<IEnumerable<TEntity>> SessionAsync<TEntity>(Func<TDbContext, IQueryable<TEntity>> callback) where TEntity : IEntity
        => this.SessionAsync<TDbContext, TEntity>(callback);

    public TResultType Session<TResultType>(Func<TDbContext, TResultType> callback)
        => this.Session<TDbContext, TResultType>(callback);

    public IEnumerable<TEntity> Session<TEntity>(Func<TDbContext, IQueryable<TEntity>> callback) where TEntity : IEntity
        => this.Session<TDbContext, TEntity>(callback);

    public Task<bool> TransactionAsync(Func<TDbContext, bool> callbackTransactionBody, bool throwException = false)
        => this.TransactionAsync<TDbContext>(callbackTransactionBody, throwException);

    public bool Transaction(Func<TDbContext, bool> callbackTransactionBody, bool throwException = false)
        => this.Transaction<TDbContext>(callbackTransactionBody, throwException);

    public Task<TEntity?> UpdateSafeAsync<TEntity>(TEntity entity, Func<TDbContext, TEntity, bool>? callbackTransactionBody = null, bool throwException = false) where TEntity : class, IEntityGuid
        => this.UpdateSafeAsync<TDbContext, TEntity>(entity, callbackTransactionBody, throwException);
    public TEntity? UpdateSafe<TEntity>(TEntity entity, Func<TDbContext, TEntity, bool>? callbackTransactionBody = null, bool throwException = false) where TEntity : class, IEntityGuid
        => this.UpdateSafe<TDbContext, TEntity>(entity, callbackTransactionBody, throwException);
    

    public override string ToString()
    {
        return Name;
    }

    //public void Dispose()
    //{
    //    Ctx?.Dispose();
    //    Ctx = null;

    //    //CloseDbContext(ref Ctx);
    //}
}