using System.Diagnostics.CodeAnalysis;

namespace Pilz.Data;

public abstract class DataManager : IDataManager
{
    protected abstract void UpdateEntity<T>(T obj) where T : class, IDataObject;
    protected abstract IQueryable<T> GetEntitySet<T>() where T : class, IDataObject;
    protected abstract T? FindEntity<T>(int id) where T : class, IDataObject;
    protected abstract void RemoveEntity<T>(T obj) where T : class, IDataObject;
    protected abstract void SaveChanges();

    public virtual IQueryable<T> Get<T>() where T : class, IDataObject
    {
        return GetEntitySet<T>();
    }

    public virtual T FindOrNew<T>(int? id) where T : class, IDataObject
    {
        if (Find<T>(id) is not T obj)
            obj = Activator.CreateInstance<T>();
        return obj;
    }

    public virtual T FindOrNew<T>(int id) where T : class, IDataObject
    {
        if (Find<T>(id) is not T obj)
            obj = Activator.CreateInstance<T>();
        return obj;
    }

    public virtual T? FindOrNull<T>(int? id) where T : class, IDataObject
    {
        if (id == null || id == 0)
            return default;
        return Find<T>(id);
    }

    public virtual T? FindOrNull<T>(int id) where T : class, IDataObject
    {
        if (id == 0)
            return default;
        return Find<T>(id);
    }

    public virtual T? Find<T>(int? id) where T : class, IDataObject
    {
        if (id == null)
            return default;
        return Find<T>(id.Value);
    }

    public virtual T? Find<T>(int id) where T : class, IDataObject
    {
        return FindEntity<T>(id);
    }

    public virtual bool FindOrNew<T>(int? id, [NotNullWhen(true)] out T? obj) where T : class, IDataObject
    {
        if (id == null || id == 0)
        {
            obj = Activator.CreateInstance<T>();
            return obj != null;
        }
        return Find(id.Value, out obj);
    }

    public virtual bool FindOrNew<T>(int id, [NotNullWhen(true)] out T? obj) where T : class, IDataObject
    {
        if (id == 0)
        {
            obj = Activator.CreateInstance<T>();
            return obj != null;
        }
        return Find(id, out obj);
    }

    public virtual bool FindOrNull<T>(int? id, out T? obj) where T : class, IDataObject
    {
        if (id == null || id == 0)
        {
            obj = default;
            return true;
        }
        return Find(id.Value, out obj);
    }

    public virtual bool FindOrNull<T>(int id, out T? obj) where T : class, IDataObject
    {
        if (id == 0)
        {
            obj = default;
            return true;
        }
        return Find(id, out obj);
    }

    public virtual bool Find<T>(int id, [NotNullWhen(true)] out T? obj) where T : class, IDataObject
    {
        if (Find<T>(id) is T t)
        {
            obj = t;
            return true;
        }
        obj = default;
        return false;
    }

    public virtual bool Find<T>(int? id, [NotNullWhen(true)] out T? obj) where T : class, IDataObject
    {
        if (Find<T>(id) is T t)
        {
            obj = t;
            return true;
        }
        obj = default;
        return false;
    }

    public virtual void Delete<T>(int id) where T : class, IDataObject
    {
        Delete<T>(id, false);
    }

    public virtual void Delete<T>(int id, bool save) where T : class, IDataObject
    {
        if (Find(id, out T? obj))
            Delete(obj, save);
    }

    public virtual void Delete<T>(T? obj) where T : class, IDataObject
    {
        Delete(obj, false);
    }

    public virtual void Delete<T>(T? obj, bool save) where T : class, IDataObject
    {
        if (obj != null)
        {
            RemoveEntity(obj);
            if (save) Save();
        }
    }

    public virtual void Save<T>(T obj) where T : class, IDataObject
    {
        Save(obj, false);
    }

    public virtual void Save<T>(T obj, bool save) where T : class, IDataObject
    {
        UpdateEntity(obj);
        if (save) Save();
    }

    public virtual void Save()
    {
        SaveChanges();
    }
}
