using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Pgo.Backend.Config;
using Pgo.Backend.Helpers;
using Pgo.Backend.Models;
using Pgo.Backend.Services;

namespace Pgo.Backend.Controllers;

// [Authorize]
[ApiController]
public class RemoteResourcesController(ILogger<RemoteResourcesController> logger, IOptions<PgoConfig> pgoConfig, IResourcesBackgroundService jobService, IDocumentService documentService) : ControllerBase
{
    private readonly bool _isAsyncResponsePreferredByServer = false;
    private readonly ILogger<RemoteResourcesController> _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    private readonly PgoConfig _config = pgoConfig?.Value ?? throw new ArgumentNullException(nameof(pgoConfig));
    private readonly IResourcesBackgroundService _jobs = jobService ?? throw new ArgumentNullException(nameof(jobService));
    private readonly IDocumentService _service = documentService ?? throw new ArgumentNullException(nameof(documentService));

    // <summary>
    /// Example endpoint for remote PGO's to resolve the DocumentReference.content.attachment.url to download a PDF.
    /// In <see cref="FrontendResourcesController"/> there's a simplifed version. On this async-endpoint however we
    /// do support the Asynchronous Interaction Request Pattern described at https://build.fhir.org/async.html too.
    /// </summary>
    [HttpGet("api/resources/genereer_dossier_pdf")] // https://localhost:5001/api/resources/genereer_dossier_pdf
    public async Task<IActionResult> DownloadGeneratedPdfAsync()
    {
        // DEMO: Some sanity checks.
        string patientId = User.FindFirst("sub")?.Value ?? "voorbeeldpatient-01";
        if (string.IsNullOrWhiteSpace(patientId))
        {
            _logger.LogError($"{nameof(DownloadGeneratedPdfAsync)} failed to generate PDF: patient user {patientId} unknown.");
            return BadRequest("Patiëntgebruiker onbekend of geen toestemming gegeven.");
        }

        // DEMO: Check if the server or client prefers an asynchronous response.
        var request = new DocumentRequest(patientId, _config.DossierOverviewUrl);
        var isAsyncResponsePreferredByClient = Request.Headers["Prefer"].ToString().Equals("respond-async", StringComparison.OrdinalIgnoreCase);
        if (isAsyncResponsePreferredByClient || _isAsyncResponsePreferredByServer)
        {
            // Asynchronous retrieval by client, returns an endpoint for status polling.
            // Return: HTTP 202 Accepted, Location: /api/resources/status/123
            // Optionally include an OperationOutcome in body.
            var status = _jobs.QueueJob(request);
            Response.Headers.Append("Retry-After", status.RetryAfterSeconds.ToString());
            return Accepted($"{_config.BasePath}/api/resources/status/{status.Id}");
        }
        else
        {
            // Synchronous retrieval by client, waits for the response and returns bundle directly.
            // Return: HTTP 200 OK. Body includes a Searchset or Document bundle.
            byte[] bytes = await _service.GetDocumentAsync(request);
            return File(bytes, "application/pdf");
        }
    }

    /// <summary>
    /// Endpoint for remote PGO's to poll the status of a job started at <see cref="DownloadGeneratedPdfAsync"/>.
    /// Do a sanity check and verify the user is allowed to poll the status and retrieve the result.
    /// </summary>
    [ActionName(nameof(GetJobStatus))]
    [HttpGet("api/resources/status/{jobId}")] // https://localhost:5001/api/resources/status/123
    public IActionResult GetJobStatus(string jobId)
    {
        var status = _jobs.GetJobStatus(jobId);

        if (status?.Result == null)
        {
            return this.WithFhirResponseOperationOutcomeNotFound(status?.ErrorMessage);
        }

        if (status.IsCompleted)
        {
            switch (status.ResultType)
            {
                case Type pdfBytes when pdfBytes == typeof(byte[]):
                    return File(status.Result as byte[] ?? Array.Empty<byte>(), "application/pdf");

                default:
                    return Accepted(status.Result);
            }
        }

        Response.Headers.Append("Retry-After", status.ApplyExponentialBackoff().RetryAfterSeconds.ToString());
        return StatusCode(202, new { message = "Processing", jobId });
    }
}
