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

namespace Journal.Core.Services.Todos;

public class TodoService(ITodoRepository repo) : ITodoService
{
    private readonly ITodoRepository _repo = repo ?? throw new ArgumentNullException(nameof(repo));

    private static TodoItemDto MapItem(TodoItem i) => new(
        i.Id,
        i.ListId,
        i.Text,
        i.Done,
        i.SortOrder
    );

    private TodoListDto MapList(TodoList l)
    {
        var items = _repo.GetItemsByListId(l.Id);
        return new TodoListDto(
            l.Id,
            l.Label,
            l.CreatedAt,
            [.. items.Select(MapItem)]
        );
    }

    public List<TodoListDto> GetAllLists()
    {
        var lists = _repo.GetAllLists();
        return [.. lists.Select(MapList)];
    }

    public TodoListDto? GetListById(Guid id)
    {
        var l = _repo.GetListById(id);
        return l is null ? null : MapList(l);
    }

    public TodoListDto CreateList(CreateTodoListDto dto)
    {
        ArgumentNullException.ThrowIfNull(dto);

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

        var list = new TodoList(dto.Label);
        _repo.AddList(list);
        return new TodoListDto(list.Id, list.Label, list.CreatedAt, []);
    }

    public bool UpdateList(Guid id, UpdateTodoListDto dto)
    {
        ArgumentNullException.ThrowIfNull(dto);
        if (dto.Label is not null && string.IsNullOrWhiteSpace(dto.Label))
            throw new ValidationException("Label cannot be empty");

        return _repo.UpdateList(id, dto.Label?.Trim());
    }

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

    public TodoItemDto CreateItem(CreateTodoItemDto dto)
    {
        ArgumentNullException.ThrowIfNull(dto);

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

        if (dto.ListId == Guid.Empty)
            throw new ValidationException("ListId is required");

        var item = new TodoItem(dto.ListId, dto.Text, dto.SortOrder ?? 0);
        _repo.AddItem(item);
        return MapItem(item);
    }

    public bool UpdateItem(Guid id, UpdateTodoItemDto dto)
    {
        ArgumentNullException.ThrowIfNull(dto);
        if (dto.Text is not null && string.IsNullOrWhiteSpace(dto.Text))
            throw new ValidationException("Text cannot be empty");

        return _repo.UpdateItem(id, dto.Text?.Trim(), dto.Done, dto.SortOrder);
    }

    public bool RemoveItem(Guid id) => _repo.RemoveItem(id);
}
