using Pilz.Data;
using Pilz.Extensions.Reflection;
using Pilz.Net.Extensions;
using System.Diagnostics;

namespace Pilz.Net.Api.Server;

public abstract class BaseHandler<TEntity, TUpdateMsg>(IApiServer server)
    : IApiHandlerInitializer
    where TEntity : class, IDataObject
    where TUpdateMsg : ApiMessage
{
    public class EntityUpdate(TEntity entity, TUpdateMsg message, ApiRequestInfo reqest)
    {
        public TEntity Entity { get; } = entity;
        public TUpdateMsg Message { get; } = message;
        public ApiRequestInfo Request { get; } = reqest;
    }

    /// <summary>
    /// Gets the base route (endpoint) for the most API calls.
    /// </summary>
    public abstract string Route { get; }
    protected virtual bool RegisterGet => true;
    protected virtual bool RegisterPut => true;
    protected virtual bool RegisterDelete => true;

    public virtual void Initialize(IApiServer server)
    {
        var t = GetType();
        if (RegisterGet)
            server.RegisterHandler(t.GetMethod(nameof(Get))!.CreateDelegate(this), new(Route + "/{id}", "GET"), Debugger.IsAttached);
        if (RegisterPut)
            server.RegisterHandler(t.GetMethod(nameof(Put))!.CreateDelegate(this), new(Route + "/{id}", "PUT"), Debugger.IsAttached);
        if (RegisterDelete)
            server.RegisterHandler(t.GetMethod(nameof(Delete))!.CreateDelegate(this), new(Route + "/{id}", "DELETE"), Debugger.IsAttached);
    }

    public virtual ApiResult Get(int id)
    {
        if (!server.Manager.Find(id, out TEntity? entity))
            return ApiResult.NotFound();
        return ToClient(entity).ToItemResult();
    }

    public virtual ApiResult Put(int id, TUpdateMsg msg, ApiRequestInfo req)
    {
        if (!server.Manager.Find(id, out TEntity? entity))
            return ApiResult.NotFound();
        var update = new EntityUpdate(entity, msg, req);
        if (UpdateEntity(update) is ApiResult result)
            return result;
        if (OnSave(update) is ApiResult result2)
            return result2;
        return ToClient(entity).ToItemResult();
    }

    public virtual ApiResult Delete(int id)
    {
        if (server.Manager.Find<TEntity>(id, out var entity) && OnDelete(entity) is ApiResult result)
            return result;
        return ApiResult.Ok();
    }

    protected virtual ApiResult? OnDelete(TEntity entity)
    {
        server.Manager.Delete(entity, true);
        return null;
    }

    protected virtual ApiResult? OnSave(EntityUpdate update)
    {
        server.Manager.Save(update.Entity, true);
        return null;
    }

    protected virtual TEntity CreateNewEntity(TUpdateMsg msg)
    {
        return Activator.CreateInstance<TEntity>();
    }

    protected abstract ApiResult? UpdateEntity(EntityUpdate update);

    protected abstract IDataObject ToClient(TEntity entity);
}
