using OmsorgCore.Application.Abstractions;
using OmsorgCore.Domain.Entities;

namespace OmsorgCore.Application.Services;

public class ValueListService : IValueListService
{
    private readonly IValueListRepository _valueListRepository;
    private readonly IEnumerable<IValueListUsageChecker> _usageCheckers;

    public ValueListService(IValueListRepository valueListRepository, IEnumerable<IValueListUsageChecker> usageCheckers)
    {
        _valueListRepository = valueListRepository;
        _usageCheckers = usageCheckers;
    }

    public Task<IReadOnlyList<ValueList>> GetListsAsync(CancellationToken cancellationToken = default)
        => _valueListRepository.GetAllListsAsync(cancellationToken);

    public Task<IReadOnlyList<ValueListItem>> GetItemsAsync(string key, CancellationToken cancellationToken = default)
        => _valueListRepository.GetItemsAsync(key, cancellationToken);

    public async Task<ValueListItem> CreateItemAsync(string key, string value, int sortOrder, bool isDefault, bool isInitial, bool isTerminal, bool triggersFollowUp, CancellationToken cancellationToken = default)
    {
        var list = await _valueListRepository.GetListByKeyAsync(key, cancellationToken)
            ?? throw new InvalidOperationException($"Unbekannte Auswahlliste '{key}'.");

        var item = new ValueListItem
        {
            ValueListId = list.Id,
            Value = value,
            SortOrder = sortOrder,
            IsDefault = isDefault,
            IsInitial = isInitial,
            IsTerminal = isTerminal,
            TriggersFollowUp = triggersFollowUp
        };

        await _valueListRepository.AddItemAsync(item, cancellationToken);
        await _valueListRepository.SaveChangesAsync(cancellationToken);
        return item;
    }

    public async Task<ValueListItem?> UpdateItemAsync(Guid id, string value, int sortOrder, bool isDefault, bool isInitial, bool isTerminal, bool triggersFollowUp, CancellationToken cancellationToken = default)
    {
        var item = await _valueListRepository.GetItemByIdAsync(id, cancellationToken);
        if (item is null)
        {
            return null;
        }

        item.Value = value;
        item.SortOrder = sortOrder;
        item.IsDefault = isDefault;
        item.IsInitial = isInitial;
        item.IsTerminal = isTerminal;
        item.TriggersFollowUp = triggersFollowUp;

        await _valueListRepository.UpdateItemAsync(item, cancellationToken);
        await _valueListRepository.SaveChangesAsync(cancellationToken);
        return item;
    }

    public async Task<DeleteValueListItemResult> DeleteItemAsync(Guid id, CancellationToken cancellationToken = default)
    {
        var item = await _valueListRepository.GetItemByIdAsync(id, cancellationToken);
        if (item is null)
        {
            return DeleteValueListItemResult.Fail(DeleteValueListItemFailureReason.ItemNotFound);
        }

        var usages = await FindUsagesAsync(item, cancellationToken);
        if (usages.Count > 0)
        {
            return DeleteValueListItemResult.Fail(DeleteValueListItemFailureReason.InUse, usages);
        }

        await _valueListRepository.RemoveItemAsync(item, cancellationToken);
        await _valueListRepository.SaveChangesAsync(cancellationToken);
        return DeleteValueListItemResult.Ok();
    }

    public async Task<IReadOnlyList<ValueListUsageEntry>> GetUsagesAsync(Guid id, CancellationToken cancellationToken = default)
    {
        var item = await _valueListRepository.GetItemByIdAsync(id, cancellationToken);
        return item is null ? Array.Empty<ValueListUsageEntry>() : await FindUsagesAsync(item, cancellationToken);
    }

    public Task<IReadOnlyList<ValueListItemTransition>> GetTransitionsAsync(string key, CancellationToken cancellationToken = default)
        => _valueListRepository.GetTransitionsAsync(key, cancellationToken);

    public async Task ReplaceTransitionsAsync(string key, IEnumerable<(Guid FromItemId, Guid ToItemId)> transitions, CancellationToken cancellationToken = default)
    {
        await _valueListRepository.ReplaceTransitionsAsync(key, transitions, cancellationToken);
        await _valueListRepository.SaveChangesAsync(cancellationToken);
    }

    private async Task<IReadOnlyList<ValueListUsageEntry>> FindUsagesAsync(ValueListItem item, CancellationToken cancellationToken)
    {
        var checker = _usageCheckers.FirstOrDefault(c => c.Key == item.ValueList.Key);
        return checker is null
            ? Array.Empty<ValueListUsageEntry>()
            : await checker.FindUsagesAsync(item.Id, item.Value, cancellationToken);
    }
}
