using MangaReader.Core.Data;
using MangaReader.Core.Metadata;
using Microsoft.EntityFrameworkCore;
using System.Text.RegularExpressions;

namespace MangaReader.Core.Pipeline;

public partial class MangaPipeline(MangaContext context) : IMangaPipeline
{
    public async Task RunAsync(MangaPipelineRequest request)
    {
        string sourceName = request.SourceName;
        SourceManga sourceManga = request.SourceManga;

        Source source = await GetOrAddSourceAsync(sourceName);
        Manga manga = await GetOrAddMangaAsync(sourceManga);

        foreach (SourceMangaTitle alternateTitle in sourceManga.AlternateTitles)
        {
            await AddTitleAsync(manga, alternateTitle);
        }

        foreach (string genre in sourceManga.Genres)
        {
            await LinkGenreAsync(manga, genre);
        }

        foreach (SourceMangaChapter chapter in sourceManga.Chapters)
        {
            await AddChapterAsync(manga, chapter);
        }

        context.SaveChanges();
    }

    private async Task<Source> GetOrAddSourceAsync(string sourceName)
    {
        Source? source = await context.Sources.FirstOrDefaultAsync(s => s.Name == sourceName);

        if (source != null)
            return source;

        source = new()
        {
            Name = sourceName
        };

        context.Sources.Add(source);

        return source;
    }

    private async Task<Manga> GetOrAddMangaAsync(SourceManga sourceManga)
    {
        Manga? manga = await context.Mangas.FirstOrDefaultAsync(manga => manga.Title == sourceManga.Title);

        if (manga != null)
            return manga;

        manga = new()
        {
            Title = sourceManga.Title,
            Slug = GenerateSlug(sourceManga.Title),
        };

        context.Add(manga);

        return manga;
    }

    private static string GenerateSlug(string title)
    {
        title = title.ToLowerInvariant();
        title = RemoveInvalidCharsRegex().Replace(title, ""); // remove invalid chars
        title = RemoveSpacesWithDashRegex().Replace(title, "-");         // replace spaces with dash

        return title.Trim('-');
    }

    [GeneratedRegex(@"[^a-z0-9\s-]")]
    private static partial Regex RemoveInvalidCharsRegex();

    [GeneratedRegex(@"\s+")]
    private static partial Regex RemoveSpacesWithDashRegex();

    private async Task AddTitleAsync(Manga manga, SourceMangaTitle sourceMangaTitle)
    {
        MangaTitle? mangaTitle = await context.MangaTitles.FirstOrDefaultAsync(mt =>
            mt.Manga == manga && mt.Name == sourceMangaTitle.Title);

        if (mangaTitle != null)
            return;

        mangaTitle = new()
        {
            Manga = manga,
            Name = sourceMangaTitle.Title,
        };

        context.MangaTitles.Add(mangaTitle);
    }

    private async Task LinkGenreAsync(Manga manga, string genreName)
    {
        Genre genre = await GetOrAddGenreAsync(genreName);

        MangaGenre? mangaGenre = await context.MangaGenres.FirstOrDefaultAsync(x => x.Manga == manga && x.Genre == genre);

        if (mangaGenre != null)
            return;

        mangaGenre = new()
        {
            Manga = manga,
            Genre = genre
        };

        context.MangaGenres.Add(mangaGenre);
    }

    private async Task<Genre> GetOrAddGenreAsync(string genreName)
    {
        Genre? genre = await context.Genres.FirstOrDefaultAsync(x => x.Name == genreName);

        if (genre != null)
            return genre;

        genre = new()
        {
            Name = genreName,
        };

        await context.Genres.AddAsync(genre);

        return genre;
    }

    private async Task AddChapterAsync(Manga manga, SourceMangaChapter sourceeMangaChapter)
    {
        MangaChapter mangaChapter = await context.MangaChapters.FirstOrDefaultAsync(x => x.ChapterNumber == sourceeMangaChapter.Number)
            ?? AddMangaChapter(manga, sourceeMangaChapter);

        if (mangaChapter.VolumeNumber is null && sourceeMangaChapter.Volume is not null)
        {
            mangaChapter.VolumeNumber = sourceeMangaChapter.Volume;
        }

        if (mangaChapter.Title is null && sourceeMangaChapter.Title is not null)
        {
            mangaChapter.Title = sourceeMangaChapter.Title;
        }
    }

    private MangaChapter AddMangaChapter(Manga manga, SourceMangaChapter sourceeMangaChapter)
    {
        MangaChapter mangaChapter = new()
        {
            Manga = manga,
            ChapterNumber = sourceeMangaChapter.Number
        };

        context.MangaChapters.Add(mangaChapter);

        return mangaChapter;
    }
}
