using MemberCenter.Api.Contracts;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using OpenIddict.Abstractions;
using System.Text.Json;

namespace MemberCenter.Api.Controllers;

[ApiController]
[Route("admin/oauth-clients")]
[Authorize(Policy = "Admin")]
public class AdminOAuthClientsController : ControllerBase
{
    private readonly IOpenIddictApplicationManager _applicationManager;

    public AdminOAuthClientsController(IOpenIddictApplicationManager applicationManager)
    {
        _applicationManager = applicationManager;
    }

    [HttpGet]
    public async Task<IActionResult> List()
    {
        var results = new List<object>();
        await foreach (var application in _applicationManager.ListAsync())
        {
            results.Add(new
            {
                id = await _applicationManager.GetIdAsync(application),
                name = await _applicationManager.GetDisplayNameAsync(application),
                client_id = await _applicationManager.GetClientIdAsync(application),
                client_type = await _applicationManager.GetClientTypeAsync(application),
                redirect_uris = await _applicationManager.GetRedirectUrisAsync(application),
                properties = await _applicationManager.GetPropertiesAsync(application)
            });
        }

        return Ok(results);
    }

    [HttpPost]
    public async Task<IActionResult> Create([FromBody] OAuthClientRequest request)
    {
        var descriptor = new OpenIddictApplicationDescriptor
        {
            ClientId = Guid.NewGuid().ToString("N"),
            DisplayName = request.Name,
            ClientType = request.ClientType,
            Permissions =
            {
                OpenIddictConstants.Permissions.Endpoints.Authorization,
                OpenIddictConstants.Permissions.Endpoints.Token,
                OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode,
                OpenIddictConstants.Permissions.GrantTypes.RefreshToken,
                OpenIddictConstants.Permissions.ResponseTypes.Code,
                OpenIddictConstants.Permissions.Scopes.Email,
                OpenIddictConstants.Permissions.Scopes.Profile,
                "scp:openid"
            }
        };

        foreach (var uri in request.RedirectUris)
        {
            descriptor.RedirectUris.Add(new Uri(uri));
        }

        descriptor.Properties["tenant_id"] = JsonSerializer.SerializeToElement(request.TenantId.ToString());

        await _applicationManager.CreateAsync(descriptor);

        return Created("/admin/oauth-clients", new
        {
            descriptor.ClientId,
            descriptor.DisplayName,
            descriptor.ClientType,
            redirect_uris = descriptor.RedirectUris.Select(u => u.ToString())
        });
    }

    [HttpGet("{id}")]
    public async Task<IActionResult> Get(string id)
    {
        var app = await _applicationManager.FindByIdAsync(id);
        if (app is null)
        {
            return NotFound();
        }

        return Ok(new
        {
            id,
            name = await _applicationManager.GetDisplayNameAsync(app),
            client_id = await _applicationManager.GetClientIdAsync(app),
            client_type = await _applicationManager.GetClientTypeAsync(app),
            redirect_uris = await _applicationManager.GetRedirectUrisAsync(app),
            properties = await _applicationManager.GetPropertiesAsync(app)
        });
    }

    [HttpPut("{id}")]
    public async Task<IActionResult> Update(string id, [FromBody] OAuthClientRequest request)
    {
        var app = await _applicationManager.FindByIdAsync(id);
        if (app is null)
        {
            return NotFound();
        }

        var descriptor = new OpenIddictApplicationDescriptor();
        await _applicationManager.PopulateAsync(descriptor, app);

        descriptor.DisplayName = request.Name;
        descriptor.ClientType = request.ClientType;
        descriptor.RedirectUris.Clear();
        foreach (var uri in request.RedirectUris)
        {
            descriptor.RedirectUris.Add(new Uri(uri));
        }
        descriptor.Properties["tenant_id"] = JsonSerializer.SerializeToElement(request.TenantId.ToString());

        await _applicationManager.UpdateAsync(app, descriptor);

        return Ok(new
        {
            id,
            descriptor.DisplayName,
            descriptor.ClientType,
            redirect_uris = descriptor.RedirectUris.Select(u => u.ToString())
        });
    }

    [HttpDelete("{id}")]
    public async Task<IActionResult> Delete(string id)
    {
        var app = await _applicationManager.FindByIdAsync(id);
        if (app is null)
        {
            return NotFound();
        }

        await _applicationManager.DeleteAsync(app);
        return NoContent();
    }
}
