using API.APIEndpointRecords;
using API.Schema;
using Asp.Versioning;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Transforms;
using static Microsoft.AspNetCore.Http.StatusCodes;

namespace API.Controllers;

[ApiVersion(2)]
[ApiController]
[Route("v{v:apiVersion}/[controller]")]
public class MangaController(PgsqlContext context) : Controller
{
    /// <summary>
    /// Returns all cached Manga
    /// </summary>
    /// <response code="200"></response>
    [HttpGet]
    [ProducesResponseType<Manga[]>(Status200OK, "application/json")]
    public IActionResult GetAllManga()
    {
        Manga[] ret = context.Manga.ToArray();
        return Ok(ret);
    }
    
    /// <summary>
    /// Returns all cached Manga with IDs
    /// </summary>
    /// <param name="ids">Array of Manga-IDs</param>
    /// <response code="200"></response>
    [HttpPost("WithIDs")]
    [ProducesResponseType<Manga[]>(Status200OK, "application/json")]
    public IActionResult GetManga([FromBody]string[] ids)
    {
        Manga[] ret = context.Manga.Where(m => ids.Contains(m.MangaId)).ToArray();
        return Ok(ret);
    }

    /// <summary>
    /// Return Manga with ID
    /// </summary>
    /// <param name="id">Manga-ID</param>
    /// <response code="200"></response>
    /// <response code="404">Manga with ID not found</response>
    [HttpGet("{id}")]
    [ProducesResponseType<Manga>(Status200OK, "application/json")]
    [ProducesResponseType(Status404NotFound)]
    public IActionResult GetManga(string id)
    {
        Manga? ret = context.Manga.Find(id);
        if (ret is null)
            return NotFound();
        return Ok(ret);
    }

    /// <summary>
    /// Delete Manga with ID
    /// </summary>
    /// <param name="id">Manga-ID</param>
    /// <response code="200"></response>
    /// <response code="404">Manga with ID not found</response>
    /// <response code="500">Error during Database Operation</response>
    [HttpDelete("{id}")]
    [ProducesResponseType(Status200OK)]
    [ProducesResponseType(Status404NotFound)]
    [ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
    public IActionResult DeleteManga(string id)
    {
        try
        {
            Manga? ret = context.Manga.Find(id);
            if (ret is null)
                return NotFound();
            
            context.Remove(ret);
            context.SaveChanges();
            return Ok();
        }
        catch (Exception e)
        {
            return StatusCode(500, e.Message);
        }
    }

    /// <summary>
    /// Returns Cover of Manga
    /// </summary>
    /// <param name="id">Manga-ID</param>
    /// <param name="formatRequest">Formatting/Resizing Request</param>
    /// <response code="200">JPEG Image</response>
    /// <response code="204">Cover not loaded</response>
    /// <response code="400">The formatting-request was invalid</response>
    /// <response code="404">Manga with ID not found</response>
    [HttpPost("{id}/Cover")]
    [ProducesResponseType<byte[]>(Status200OK,"image/jpeg")]
    [ProducesResponseType(Status204NoContent)]
    [ProducesResponseType(Status400BadRequest)]
    [ProducesResponseType(Status404NotFound)]
    public IActionResult GetCover(string id, [FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)]CoverFormatRequestRecord? formatRequest)
    {
        Manga? m = context.Manga.Find(id);
        if (m is null)
            return NotFound();
        if (!System.IO.File.Exists(m.CoverFileNameInCache))
            return NoContent();

        Image image = Image.Load(m.CoverFileNameInCache);

        if (formatRequest is not null)
        {
            if(!formatRequest.Validate())
                return BadRequest();
            image.Mutate(i => i.ApplyProcessor(new ResizeProcessor(new ResizeOptions()
            {
                Mode = ResizeMode.Max,
                Size = formatRequest.size
            }, image.Size)));
        }
        
        using MemoryStream ms = new();
        image.Save(ms, new JpegEncoder(){Quality = 100});
        return File(ms.GetBuffer(), "image/jpeg");
    }

    /// <summary>
    /// Returns all Chapters of Manga
    /// </summary>
    /// <param name="id">Manga-ID</param>
    /// <response code="200"></response>
    /// <response code="404">Manga with ID not found</response>
    [HttpGet("{id}/Chapters")]
    [ProducesResponseType<Chapter[]>(Status200OK, "application/json")]
    [ProducesResponseType(Status404NotFound)]
    public IActionResult GetChapters(string id)
    {
        Manga? m = context.Manga.Find(id);
        if (m is null)
            return NotFound();
        
        Chapter[] ret = context.Chapters.Where(c => c.ParentMangaId == m.MangaId).ToArray();
        return Ok(ret);
    }
    
    /// <summary>
    /// Returns the latest Chapter of requested Manga available on Website
    /// </summary>
    /// <param name="id">Manga-ID</param>
    /// <response code="200"></response>
    /// <response code="204">No available chapters</response>
    /// <response code="404">Manga with ID not found.</response>
    /// <response code="500">Could not retrieve the maximum chapter-number</response>
    [HttpGet("{id}/Chapter/LatestAvailable")]
    [ProducesResponseType<Chapter>(Status200OK, "application/json")]
    [ProducesResponseType(Status204NoContent)]
    [ProducesResponseType(Status404NotFound)]
    [ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
    public IActionResult GetLatestChapter(string id)
    {
        Manga? m = context.Manga.Find(id);
        if (m is null)
            return NotFound();
        
        List<Chapter> chapters = context.Chapters.Where(c => c.ParentMangaId == m.MangaId).ToList();
        if (chapters.Count == 0)
            return NoContent();
        
        Chapter? max = chapters.Max();
        if (max is null)
            return StatusCode(500, "Max chapter could not be found");
        
        return Ok(max);
    }
    
    /// <summary>
    /// Returns the latest Chapter of requested Manga that is downloaded
    /// </summary>
    /// <param name="id">Manga-ID</param>
    /// <response code="200"></response>
    /// <response code="204">No available chapters</response>
    /// <response code="404">Manga with ID not found.</response>
    /// <response code="500">Could not retrieve the maximum chapter-number</response>
    [HttpGet("{id}/Chapter/LatestDownloaded")]
    [ProducesResponseType<Chapter>(Status200OK, "application/json")]
    [ProducesResponseType(Status204NoContent)]
    [ProducesResponseType(Status404NotFound)]
    [ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
    public IActionResult GetLatestChapterDownloaded(string id)
    {
        Manga? m = context.Manga.Find(id);
        if (m is null)
            return NotFound();
        
        List<Chapter> chapters = context.Chapters.Where(c => c.ParentMangaId == m.MangaId && c.Downloaded == true).ToList();
        if (chapters.Count == 0)
            return NoContent();
        
        Chapter? max = chapters.Max();
        if (max is null)
            return StatusCode(500, "Max chapter could not be found");
        
        return Ok(max);
    }
    
    /// <summary>
    /// Configure the cut-off for Manga
    /// </summary>
    /// <param name="id">Manga-ID</param>
    /// <response code="200"></response>
    /// <response code="404">Manga with ID not found.</response>
    [HttpPatch("{id}/IgnoreChaptersBefore")]
    [ProducesResponseType<float>(Status200OK, "text/plain")]
    [ProducesResponseType(Status404NotFound)]
    public IActionResult IgnoreChaptersBefore(string id)
    {
        Manga? m = context.Manga.Find(id);
        if (m is null)
            return NotFound();
        return Ok(m.IgnoreChapterBefore);
    }
    
    /// <summary>
    /// Move the Directory the .cbz-files are located in
    /// </summary>
    /// <param name="id">Manga-ID</param>
    /// <param name="folder">New Directory-Path</param>
    /// <remarks>NOT IMPLEMENTED</remarks>
    [HttpPost("{id}/MoveFolder")]
    public IActionResult MoveFolder(string id, [FromBody]string folder)
    {
        throw new NotImplementedException();
    }
}