using System.ComponentModel.DataAnnotations;
using Journal.Core.Dtos;
using Journal.Core.Models;
using Journal.Core.Repositories;

namespace Journal.Core.Services.Fragments;

public class FragmentService(IFragmentRepository repo) : IFragmentService
{
    private readonly IFragmentRepository _repo = repo ?? throw new ArgumentNullException(nameof(repo));

    private static FragmentDto Map(Fragment f) => new(
        f.Id,
        f.Type,
        f.Description,
        f.Time,
        f.Tags != null ? [.. f.Tags] : []
    );

    public FragmentDto Create(CreateFragmentDto dto)
    {
        ArgumentNullException.ThrowIfNull(dto);

        var ctx = new ValidationContext(dto);
        Validator.ValidateObject(dto, ctx, validateAllProperties: true);

        var f = new Fragment(dto.Type, dto.Description);
        if (dto.Tags != null)
            f.Tags = [.. dto.Tags.Where(t => !string.IsNullOrWhiteSpace(t)).Select(t => t!.Trim())];

        _repo.Add(f);
        return Map(f);
    }

    public bool Update(Guid id, UpdateFragmentDto dto)
    {
        ArgumentNullException.ThrowIfNull(dto);
        if (dto.Type != null && string.IsNullOrWhiteSpace(dto.Type))
            throw new ValidationException("Type cannot be empty");
        if (dto.Description != null && string.IsNullOrWhiteSpace(dto.Description))
            throw new ValidationException("Description cannot be empty");

        var type = dto.Type?.Trim();
        var description = dto.Description?.Trim();
        var tags = dto.Tags?.Where(t => !string.IsNullOrWhiteSpace(t)).Select(t => t!.Trim()).ToList();

        return _repo.Update(id, type, description, tags, dto.Time);
    }

    public bool Remove(Guid id) => _repo.Remove(id);

    public List<FragmentDto> Search(string? type = null, string? tag = null, DateTimeOffset? timeAfter = null)
    {
        var items = _repo.Search(type, tag, timeAfter);
        return [.. items.Select(Map)];
    }

    public List<FragmentDto> GetByTag(string tag)
    {
        var items = _repo.GetByTag(tag);
        return [.. items.Select(Map)];
    }

    public List<FragmentDto> GetByType(string type)
    {
        var items = _repo.GetByType(type);
        return [.. items.Select(Map)];
    }

    public List<FragmentDto> GetAll()
    {
        var items = _repo.GetAll();
        return [.. items.Select(Map)];
    }

    public FragmentDto? GetById(Guid id)
    {
        var f = _repo.GetById(id);
        return f is null ? null : Map(f);
    }
}
