namespace HiVakil.Api.WebFramework.Bases;


public class CrudEndpoint<TEntity,TGetAllQuery,TGetOneQuery,TCreateCommand,TUpdateCommand,TDeleteCommand> where TEntity : ApiEntity, new()
{
    private readonly string _endpointName;

    public CrudEndpoint(string endpointName)
    {
        _endpointName = endpointName;
    }

    public virtual void AddRoutes(IEndpointRouteBuilder app)
    {
        var group = app.NewVersionedApi(_endpointName).MapGroup($"api/{_endpointName}");

        group.MapGet("", GetAllAsync)
            .WithDisplayName("GetAll")
            .HasApiVersion(1.0);

        group.MapGet("{id}", GetAsync)
            .WithName("GetOne")
            .HasApiVersion(1.0);

        group.MapPost("", Post)
            .HasApiVersion(1.0);

        group.MapPut("", Put)
            .HasApiVersion(1.0);

        group.MapDelete("", Delete)
            .HasApiVersion(1.0);
    }

    // GET:Get All Entity
    public virtual async Task<IResult> GetAllAsync(ISender sender , CancellationToken cancellationToken)
    {
        var res = sender.Send(Activator.CreateInstance<TGetAllQuery>(),cancellationToken);
        return TypedResults.Ok(res);
    }

    // GET:Get An Entity By Id
    public async Task<IResult> GetAsync(Guid id, ISender sender, CancellationToken cancellationToken)
        => TypedResults.Ok(sender.Send(Activator.CreateInstance<TGetOneQuery>()));

    // POST:Create Entity
    public virtual async Task<IResult> Post([FromBody] TCreateCommand ent , ISender mediator , CancellationToken cancellationToken)
    {
        return TypedResults.Ok(await mediator.Send(ent, cancellationToken));
    }

    // PUT:Update Entity
    public virtual async Task<IResult> Put([FromBody] TEntity ent , IRepositoryWrapper _repositoryWrapper, CancellationToken cancellationToken)
    {
        _repositoryWrapper.SetRepository<TEntity>().Update(ent);
        await _repositoryWrapper.SaveChangesAsync(cancellationToken);
        return TypedResults.Ok();
    }

    // DELETE:Delete Entity
    public virtual async Task<IResult> Delete(Guid id,IRepositoryWrapper _repositoryWrapper, CancellationToken cancellationToken)
    {
        var ent = await _repositoryWrapper.SetRepository<TEntity>().GetByIdAsync(cancellationToken, id);
        _repositoryWrapper.SetRepository<TEntity>().Delete(ent);
        await _repositoryWrapper.SaveChangesAsync(cancellationToken);
        return TypedResults.Ok();
    }
}

[ApiController]
//[AllowAnonymous]
[ApiResultFilter]
[Route("api/v{version:apiVersion}/[controller]")] // api/v1/[controller]
public class BaseController : ControllerBase
{
    //public UserRepository UserRepository { get; set; } => property injection
    public bool UserIsAutheticated => HttpContext.User.Identity.IsAuthenticated;
}

[Authorize(AuthenticationSchemes = "Bearer")]
public class CrudController<TDto, TEntity> : BaseController
    where TDto : BaseDto<TDto, TEntity>, new()
    where TEntity : ApiEntity, new()
{
    protected readonly IRepositoryWrapper _repositoryWrapper;

    public CrudController(IRepositoryWrapper repositoryWrapper)
    {
        _repositoryWrapper = repositoryWrapper;
    }

    // GET:Get All Entity
    [HttpGet]
    public virtual async Task<IActionResult> GetAllAsync(CancellationToken cancellationToken)
    {
        var projectTo = typeof(TDto).BaseType?.GetProperty("ProjectToDto")?.GetValue(null, null);
        if (projectTo != null)
        {
            var exprss = projectTo as Expression<Func<TEntity, TDto>>;
            var entites = await _repositoryWrapper
                .SetRepository<TEntity>()
                .TableNoTracking
                .Select(exprss)
                .ToListAsync(cancellationToken);
            return Ok(entites);
        }

        throw new BaseApiException("ProjectTo Not Found");
    }

    // GET:Get An Entity By Id
    [HttpGet("{id}")]
    public virtual async Task<IActionResult> GetAsync(string id, CancellationToken cancellationToken)
    {
        var ent = await _repositoryWrapper.SetRepository<TEntity>().GetByIdAsync(cancellationToken, id);
        var dto = ent.Adapt<TDto>();
        return Ok(dto);
    }

    // POST:Add New Entity
    [HttpPost]
    public virtual async Task<IActionResult> PostOrginal([FromBody] TEntity ent, CancellationToken cancellationToken)
    {
        _repositoryWrapper.SetRepository<TEntity>().Add(ent);
        await _repositoryWrapper.SaveChangesAsync(cancellationToken);
        return Ok(ent);
    }

    // POST:Add New Entity By Dto
    [HttpPost("Dto")]
    public async Task<IActionResult> PostDto([FromBody] TDto dto, CancellationToken cancellationToken)
    {
        _repositoryWrapper
            .SetRepository<TEntity>()
            .Add(dto.ToEntity());
        await _repositoryWrapper.SaveChangesAsync(cancellationToken);
        return Ok();
    }

    // PUT:Update Entity
    [HttpPut]
    public virtual async Task<IActionResult> Put([FromBody] TEntity ent, CancellationToken cancellationToken)
    {
        _repositoryWrapper
            .SetRepository<TEntity>()
            .Update(ent);
        await _repositoryWrapper.SaveChangesAsync(cancellationToken);
        return Ok();
    }

    // DELETE:Delete Entity
    [HttpDelete]
    [Route("{id:int}")]
    public virtual async Task<IActionResult> Delete(int id, CancellationToken cancellationToken)
    {
        var ent = await _repositoryWrapper
            .SetRepository<TEntity>()
            .GetByIdAsync(cancellationToken, id);
        _repositoryWrapper.SetRepository<TEntity>().Delete(ent);
        await _repositoryWrapper.SaveChangesAsync(cancellationToken);
        return Ok();
    }
}

[Authorize(AuthenticationSchemes = "Bearer")]
public class CrudController<TEntity> : BaseController
    where TEntity : ApiEntity, new()
{
    protected readonly IRepositoryWrapper _repositoryWrapper;

    public CrudController(IRepositoryWrapper repositoryWrapper)
    {
        _repositoryWrapper = repositoryWrapper;
    }

    // GET:Get All Entity
    [HttpGet]
    public virtual async Task<IActionResult> GetAllAsync()
    {
        return Ok(await _repositoryWrapper.SetRepository<TEntity>().TableNoTracking.ToListAsync());
    }

    // GET:Get An Entity By Id
    [HttpGet("{id}")]
    public async Task<IActionResult> GetAsync(int id, CancellationToken cancellationToken)
    {
        var ent = await _repositoryWrapper.SetRepository<TEntity>().GetByIdAsync(cancellationToken, id);
        return Ok(ent);
    }

    // POST:Add New Entity
    [HttpPost]
    public virtual async Task<IActionResult> Post([FromBody] TEntity ent, CancellationToken cancellationToken)
    {
        _repositoryWrapper.SetRepository<TEntity>().Add(ent);
        await _repositoryWrapper.SaveChangesAsync(cancellationToken);
        return Ok(ent);
    }

    // PUT:Update Entity
    [HttpPut]
    public virtual async Task<IActionResult> Put([FromBody] TEntity ent, CancellationToken cancellationToken)
    {
        _repositoryWrapper.SetRepository<TEntity>().Update(ent);
        await _repositoryWrapper.SaveChangesAsync(cancellationToken);
        return Ok();
    }

    // DELETE:Delete Entity
    [HttpDelete("{id}")]
    public virtual async Task<IActionResult> Delete(int id, CancellationToken cancellationToken)
    {
        var ent = await _repositoryWrapper.SetRepository<TEntity>().GetByIdAsync(cancellationToken, id);
        _repositoryWrapper.SetRepository<TEntity>().Delete(ent);
        await _repositoryWrapper.SaveChangesAsync(cancellationToken);
        return Ok();
    }
}