using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Kavita.API.Database;
using Kavita.API.Services;
using Kavita.Common.Extensions;
using Kavita.Models.Builders;
using Kavita.Models.Entities.Person;

namespace Kavita.Services;

public class PersonService(IUnitOfWork unitOfWork): IPersonService
{

    public async Task MergePeopleAsync(Person src, Person dst, CancellationToken ct = default)
    {
        if (dst.Id == src.Id) return;

        if (string.IsNullOrWhiteSpace(dst.Description) && !string.IsNullOrWhiteSpace(src.Description))
        {
            dst.Description = src.Description;
        }

        if (dst.MalId == 0 && src.MalId != 0)
        {
            dst.MalId = src.MalId;
        }

        if (dst.AniListId == 0 && src.AniListId != 0)
        {
            dst.AniListId = src.AniListId;
        }

        if (dst.HardcoverId == null && src.HardcoverId != null)
        {
            dst.HardcoverId = src.HardcoverId;
        }

        if (dst.Asin == null && src.Asin != null)
        {
            dst.Asin = src.Asin;
        }

        if (dst.CoverImage == null && src.CoverImage != null)
        {
            dst.CoverImage = src.CoverImage;
        }

        MergeChapterPeople(dst, src);
        MergeSeriesMetadataPeople(dst, src);

        dst.Aliases.Add(new PersonAliasBuilder(src.Name).Build());

        foreach (var alias in src.Aliases)
        {
            dst.Aliases.Add(alias);
        }

        unitOfWork.PersonRepository.Remove(src);
        unitOfWork.PersonRepository.Update(dst);
        await unitOfWork.CommitAsync(ct);
    }

    private static void MergeChapterPeople(Person dst, Person src)
    {

        foreach (var chapter in src.ChapterPeople)
        {
            var alreadyPresent = dst.ChapterPeople
                .Any(x => x.ChapterId == chapter.ChapterId && x.Role == chapter.Role);

            if (alreadyPresent) continue;

            dst.ChapterPeople.Add(new ChapterPeople
            {
                Role = chapter.Role,
                ChapterId = chapter.ChapterId,
                Person = dst,
                KavitaPlusConnection = chapter.KavitaPlusConnection,
                OrderWeight = chapter.OrderWeight,
            });
        }
    }

    private static void MergeSeriesMetadataPeople(Person dst, Person src)
    {
        foreach (var series in src.SeriesMetadataPeople)
        {
            var alreadyPresent = dst.SeriesMetadataPeople
                .Any(x => x.SeriesMetadataId == series.SeriesMetadataId && x.Role == series.Role);

            if (alreadyPresent) continue;

            dst.SeriesMetadataPeople.Add(new SeriesMetadataPeople
            {
                SeriesMetadataId = series.SeriesMetadataId,
                Role = series.Role,
                Person = dst,
                KavitaPlusConnection = series.KavitaPlusConnection,
                OrderWeight = series.OrderWeight,
            });
        }
    }

    public async Task<bool> UpdatePersonAliasesAsync(Person person, IList<string> aliases, CancellationToken ct = default)
    {
        var normalizedAliases = aliases
            .Select(a => a.ToNormalized())
            .Where(a => !string.IsNullOrEmpty(a) && a != person.NormalizedName)
            .ToList();

        if (normalizedAliases.Count == 0)
        {
            person.Aliases = [];
            return true;
        }

        var others = await unitOfWork.PersonRepository.GetPeopleByNames(normalizedAliases, ct: ct);
        others = others.Where(p => p.Id != person.Id).ToList();

        if (others.Count != 0) return false;

        person.Aliases = aliases.Select(a => new PersonAliasBuilder(a).Build()).ToList();

        return true;
    }
}
