using GerbilManagerWebAPI.Dtos;
using GerbilManagerWebAPI.Models;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.EntityFrameworkCore;

namespace GerbilManagerWebAPI.Endpoints
{
    /// <summary>
    /// Gerbil photo endpoints (FEAT-1b phase 2), per the contract agreed with Oscar:
    ///   GET  /gerbils/{id}/photos      -> plain array (not paged)
    ///   POST /gerbils/{id}/photos      -> multipart 'file' + optional 'caption' -> 201 PhotoDto
    ///   DELETE /photos/{id}            -> 204
    ///   GET  /photos/files/{fileName}  -> the image bytes
    /// url = "/photos/files/{fileName}"; profile photo = first by SortOrder.
    /// </summary>
    public static class PhotoEndpoints
    {
        public static IEndpointRouteBuilder MapPhotoEndpoints(this IEndpointRouteBuilder app)
        {
            app.MapGet("/gerbils/{id:guid}/photos",
                async Task<Results<Ok<List<PhotoDto>>, NotFound>> (Guid id, ApplicationContext db) =>
                {
                    if (!await db.Gerbils.AnyAsync(g => g.Id == id)) return TypedResults.NotFound();
                    var photos = await db.GerbilPhotos.AsNoTracking()
                        .Where(p => p.GerbilId == id)
                        .OrderBy(p => p.SortOrder)
                        .ToListAsync();
                    return TypedResults.Ok(photos.Select(ToDto).ToList());
                }).WithTags("Photos");

            app.MapPost("/gerbils/{id:guid}/photos",
                async Task<Results<Created<PhotoDto>, NotFound, BadRequest<string>>> (
                    Guid id, IFormFile file, [Microsoft.AspNetCore.Mvc.FromForm] string? caption,
                    ApplicationContext db, IConfiguration config, IWebHostEnvironment env) =>
                {
                    if (!await db.Gerbils.AnyAsync(g => g.Id == id)) return TypedResults.NotFound();
                    if (file is null || file.Length == 0) return TypedResults.BadRequest("No file uploaded.");

                    var ext = Path.GetExtension(file.FileName);
                    var fileName = $"{Guid.NewGuid():N}{ext}";
                    var root = PhotoRoot(config, env);
                    Directory.CreateDirectory(root);
                    await using (var stream = File.Create(Path.Combine(root, fileName)))
                        await file.CopyToAsync(stream);

                    int nextSort = (await db.GerbilPhotos.Where(p => p.GerbilId == id)
                        .Select(p => (int?)p.SortOrder).MaxAsync() ?? -1) + 1;

                    var photo = new GerbilPhoto
                    {
                        Id = Guid.NewGuid(),
                        GerbilId = id,
                        FileName = fileName,
                        Caption = caption,
                        SortOrder = nextSort,
                        CreatedAt = DateTimeOffset.UtcNow,
                    };
                    db.GerbilPhotos.Add(photo);
                    await db.SaveChangesAsync();
                    return TypedResults.Created($"/photos/{photo.Id}", ToDto(photo));
                }).WithTags("Photos").DisableAntiforgery();

            app.MapDelete("/photos/{id:guid}",
                async Task<Results<NoContent, NotFound>> (Guid id, ApplicationContext db, IConfiguration config, IWebHostEnvironment env) =>
                {
                    var photo = await db.GerbilPhotos.FirstOrDefaultAsync(p => p.Id == id);
                    if (photo is null) return TypedResults.NotFound();
                    var path = Path.Combine(PhotoRoot(config, env), photo.FileName);
                    if (File.Exists(path)) File.Delete(path);
                    db.GerbilPhotos.Remove(photo);
                    await db.SaveChangesAsync();
                    return TypedResults.NoContent();
                }).WithTags("Photos");

            app.MapGet("/photos/files/{fileName}",
                Results<PhysicalFileHttpResult, NotFound, BadRequest<string>> (string fileName, IConfiguration config, IWebHostEnvironment env) =>
                {
                    // guard against path traversal: only a bare file name is allowed
                    if (fileName.Contains('/') || fileName.Contains('\\') || fileName.Contains(".."))
                        return TypedResults.BadRequest("Invalid file name.");
                    var path = Path.Combine(PhotoRoot(config, env), fileName);
                    if (!File.Exists(path)) return TypedResults.NotFound();
                    return TypedResults.PhysicalFile(path, ContentType(fileName));
                }).WithTags("Photos");

            return app;
        }

        private static string PhotoRoot(IConfiguration config, IWebHostEnvironment env) =>
            config["Photos:RootPath"] ?? Path.Combine(env.ContentRootPath, "photo-storage");

        private static string ContentType(string fileName) => Path.GetExtension(fileName).ToLowerInvariant() switch
        {
            ".png" => "image/png",
            ".gif" => "image/gif",
            ".webp" => "image/webp",
            ".bmp" => "image/bmp",
            _ => "image/jpeg",
        };

        private static PhotoDto ToDto(GerbilPhoto p) =>
            new(p.Id, p.FileName, p.Caption, p.SortOrder, $"/photos/files/{p.FileName}");
    }
}
