using System.Security.Cryptography;
using System.Text;
using Knot.Contracts.Settings.Application.Abstractions;
using Knot.Contracts.Settings.Application.DTOs;
using Knot.Shared.Kernel.Storage;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Caching.Memory;

namespace Knot.Modules.Storage.Presentation.Endpoints;

public static class FilesEndpoints
{
    public static void MapFilesEndpoints(this WebApplication app)
    {
        // Federation remote endpoint - most specific, must be first
        app.MapGet("/api/files/remote/{domain}/{id}", async (string domain, string id, HttpClient httpClient, ISettingsService settingsService, CancellationToken ct) =>
        {
            var settings = await settingsService.GetSettingsAsync(ct);
            if (!settings.Federation.Enabled) return Results.Forbid();

            var targetUrl = $"{domain.TrimEnd('/')}/api/federation/v1/proxy/{id}";

            var request = new HttpRequestMessage(HttpMethod.Get, targetUrl);
            request.Headers.Add("X-Knot-Domain", settings.System.DomainUrl);

            using (var rsa = RSA.Create())
            {
                rsa.ImportPkcs8PrivateKey(Convert.FromBase64String(settings.Federation.PrivateKey!), out _);
                var dataToSign = Encoding.UTF8.GetBytes(id + settings.System.DomainUrl);
                var signature = Convert.ToBase64String(rsa.SignData(dataToSign, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1));
                request.Headers.Add("X-Knot-Signature", signature);
            }

            var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, ct);
            if (!response.IsSuccessStatusCode) return Results.NotFound();

            var remoteStream = await response.Content.ReadAsStreamAsync(ct);
            var contentType = response.Content.Headers.ContentType?.ToString() ?? "application/octet-stream";

            return Results.Stream(remoteStream, contentType, enableRangeProcessing: true);
        });

        // Main file download endpoint - without group prefix to avoid routing issues
        app.MapGet("/api/files/{id}", async ([FromRoute] string id, [FromServices] IFileStorageService fileStorage, [FromServices] IMemoryCache cache, HttpContext httpContext, [FromQuery] bool download = false, CancellationToken ct = default) =>
        {
            try
            {
                var cacheKey = $"file_{id}";
                if (!cache.TryGetValue(cacheKey, out (byte[] Bytes, string ContentType, string FileName) cachedData))
                {
                    var result = await fileStorage.DownloadFileAsync(id);
                    using var ms = new MemoryStream();
                    if (result.Stream.CanSeek) result.Stream.Position = 0;
                    await result.Stream.CopyToAsync(ms, ct);

                    cachedData = (ms.ToArray(), result.ContentType, result.FileName);

                    var cacheEntryOptions = new MemoryCacheEntryOptions()
                        .SetSlidingExpiration(TimeSpan.FromMinutes(30));

                    cache.Set(cacheKey, cachedData, cacheEntryOptions);
                    result.Stream.Dispose();
                }

                var outStream = new MemoryStream(cachedData.Bytes);

                if (download && !string.IsNullOrEmpty(cachedData.FileName))
                {
                    return Results.File(outStream, cachedData.ContentType, fileDownloadName: cachedData.FileName, enableRangeProcessing: true);
                }

                return Results.File(outStream, cachedData.ContentType, enableRangeProcessing: true);
            }
            catch (Exception ex)
            {
                return Results.NotFound(new { error = "Файл не найден", message = ex.Message });
            }
        });
    }
}

