using API.Schema;
using API.Schema.Jobs;
using API.Schema.MangaConnectors;
using Asp.Versioning;
using Microsoft.AspNetCore.Mvc;
using static Microsoft.AspNetCore.Http.StatusCodes;
namespace API.Controllers;
[ApiVersion(2)]
[ApiController]
[Route("v{v:apiVersion}/[controller]")]
public class SearchController(PgsqlContext context) : Controller
{
///
/// Initiate a search for a Manga on all Connectors
///
/// Name/Title of the Manga
///
/// Error during Database Operation
[HttpPost("Name")]
[ProducesResponseType(Status200OK, "application/json")]
[ProducesResponseType(Status500InternalServerError, "text/plain")]
public IActionResult SearchMangaGlobal([FromBody]string name)
{
List<(Manga, List?, List?, List?, List?)> allManga = new();
foreach (MangaConnector contextMangaConnector in context.MangaConnectors.Where(connector => connector.Enabled))
allManga.AddRange(contextMangaConnector.GetManga(name));
List retMangas = new();
foreach ((Manga? manga, List? authors, List? tags, List? links, List? altTitles) in allManga)
{
try
{
Manga? add = AddMangaToContext(manga, authors, tags, links, altTitles);
if(add is not null)
retMangas.Add(add);
}
catch (Exception e)
{
return StatusCode(500, e);
}
}
return Ok(retMangas.ToArray());
}
///
/// Initiate a search for a Manga on a specific Connector
///
/// Manga-Connector-ID
/// Name/Title of the Manga
///
/// MangaConnector with ID not found
/// MangaConnector with ID is disabled
/// Error during Database Operation
[HttpPost("{MangaConnectorName}")]
[ProducesResponseType(Status200OK, "application/json")]
[ProducesResponseType(Status404NotFound)]
[ProducesResponseType(Status406NotAcceptable)]
[ProducesResponseType(Status500InternalServerError, "text/plain")]
public IActionResult SearchManga(string MangaConnectorName, [FromBody]string name)
{
MangaConnector? connector = context.MangaConnectors.Find(MangaConnectorName);
if (connector is null)
return NotFound();
else if (connector.Enabled is false)
return StatusCode(406);
(Manga, List?, List?, List?, List?)[] mangas = connector.GetManga(name);
List retMangas = new();
foreach ((Manga? manga, List? authors, List? tags, List? links, List? altTitles) in mangas)
{
try
{
Manga? add = AddMangaToContext(manga, authors, tags, links, altTitles);
if(add is not null)
retMangas.Add(add);
}
catch (Exception e)
{
return StatusCode(500, e.Message);
}
}
return Ok(retMangas.ToArray());
}
///
/// Returns Manga from MangaConnector associated with URL
///
/// Manga-Page URL
///
/// Multiple connectors found for URL
/// No Manga at URL
/// No connector found for URL
/// Error during Database Operation
[HttpPost("Url")]
[ProducesResponseType(Status200OK, "application/json")]
[ProducesResponseType(Status300MultipleChoices)]
[ProducesResponseType(Status400BadRequest)]
[ProducesResponseType(Status404NotFound)]
[ProducesResponseType(Status500InternalServerError, "text/plain")]
public IActionResult GetMangaFromUrl([FromBody]string url)
{
List connectors = context.MangaConnectors.AsEnumerable().Where(c => c.ValidateUrl(url)).ToList();
if (connectors.Count == 0)
return NotFound();
else if (connectors.Count > 1)
return StatusCode(Status300MultipleChoices);
(Manga manga, List? authors, List? tags, List? links, List? altTitles)? x = connectors.First().GetMangaFromUrl(url);
if (x is null)
return BadRequest();
try
{
Manga? add = AddMangaToContext(x.Value.manga, x.Value.authors, x.Value.tags, x.Value.links, x.Value.altTitles);
if (add is not null)
return Ok(add);
return StatusCode(500);
}
catch (Exception e)
{
return StatusCode(500, e.Message);
}
}
private Manga? AddMangaToContext(Manga? manga, List? authors, List? tags, List? links,
List? altTitles)
{
if (manga is null)
return null;
Manga? existing = context.Mangas.Find(manga.MangaId);
if (tags is not null)
{
IEnumerable mergedTags = tags.Select(mt =>
{
MangaTag? inDb = context.Tags.Find(mt.Tag);
return inDb ?? mt;
});
manga.MangaTags = mergedTags.ToList();
IEnumerable newTags = manga.MangaTags
.Where(mt => !context.Tags.Select(t => t.Tag).Contains(mt.Tag));
context.Tags.AddRange(newTags);
}
if (authors is not null)
{
IEnumerable mergedAuthors = authors.Select(ma =>
{
Author? inDb = context.Authors.Find(ma.AuthorId);
return inDb ?? ma;
});
manga.Authors = mergedAuthors.ToList();
IEnumerable newAuthors = manga.Authors
.Where(ma => !context.Authors.Select(a => a.AuthorId).Contains(ma.AuthorId));
context.Authors.AddRange(newAuthors);
}
if (links is not null)
{
IEnumerable mergedLinks = links.Select(ml =>
{
Link? inDb = context.Links.Find(ml.LinkId);
return inDb ?? ml;
});
manga.Links = mergedLinks.ToList();
IEnumerable newLinks = manga.Links
.Where(ml => !context.Links.Select(l => l.LinkId).Contains(ml.LinkId));
context.Links.AddRange(newLinks);
}
if (altTitles is not null)
{
IEnumerable mergedAltTitles = altTitles.Select(mat =>
{
MangaAltTitle? inDb = context.AltTitles.Find(mat.AltTitleId);
return inDb ?? mat;
});
manga.AltTitles = mergedAltTitles.ToList();
IEnumerable newAltTitles = manga.AltTitles
.Where(mat => !context.AltTitles.Select(at => at.AltTitleId).Contains(mat.AltTitleId));
context.AltTitles.AddRange(newAltTitles);
}
existing?.UpdateWithInfo(manga);
if(existing is not null)
context.Mangas.Update(existing);
else
context.Mangas.Add(manga);
context.Jobs.Add(new DownloadMangaCoverJob(manga.MangaId));
context.Jobs.Add(new RetrieveChaptersJob(0, manga.MangaId));
context.SaveChanges();
return existing ?? manga;
}
}