using System.ComponentModel;
using ClaudeDo.Data.Models;
using ClaudeDo.Data.Repositories;
using ClaudeDo.Worker.Hub;
using ModelContextProtocol.Server;

namespace ClaudeDo.Worker.External;

public sealed record ListSummaryDto(string Id, string Name, string? WorkingDir, string DefaultCommitType);
public sealed record DeleteListResult(bool Deleted, string Id);

[McpServerToolType]
public sealed class ListMcpTools
{
    private readonly ListRepository _lists;
    private readonly HubBroadcaster _broadcaster;

    public ListMcpTools(ListRepository lists, HubBroadcaster broadcaster)
    {
        _lists = lists;
        _broadcaster = broadcaster;
    }

    [McpServerTool, Description("Create a new task list — the top-level grouping tasks belong to, with its own working dir, commit type, and default run config.")]
    public async Task<ListSummaryDto> CreateList(
        string name,
        [Description("Absolute local path to an existing git repository this list's tasks will run against. Not validated here — the first task run fails if the path isn't an actual git repo. Omit to run this list's tasks in a throwaway sandbox with no worktree.")]
        string? workingDir = null,
        [Description("Conventional-commit-style type prefix for this list's task commits (e.g. 'feat', 'fix'). Defaults to 'chore'.")]
        string? commitType = null,
        CancellationToken cancellationToken = default)
    {
        if (string.IsNullOrWhiteSpace(name))
            throw new InvalidOperationException("name is required.");

        var entity = new ListEntity
        {
            Id = Guid.NewGuid().ToString(),
            Name = name,
            WorkingDir = workingDir.NullIfBlank(),
            DefaultCommitType = commitType.NullIfBlank() ?? CommitTypeRegistry.DefaultType,
            CreatedAt = DateTime.UtcNow,
        };
        await _lists.AddAsync(entity, cancellationToken);
        await _broadcaster.ListUpdated(entity.Id);
        return ToDto(entity);
    }

    [McpServerTool, Description("Rename a list, or change its working dir / default commit type without recreating it. Pass null for any field to leave it unchanged.")]
    public async Task<ListSummaryDto> UpdateList(
        string listId,
        string? name = null,
        [Description("Absolute local path to an existing git repository this list's tasks will run against; not validated until the next task runs. Null leaves it unchanged; pass an empty string to clear it and switch this list to sandbox-only task runs.")]
        string? workingDir = null,
        [Description("New default commit type prefix for this list's task commits. Null leaves it unchanged.")]
        string? commitType = null,
        CancellationToken cancellationToken = default)
    {
        var entity = await _lists.GetByIdAsync(listId, cancellationToken)
            ?? throw new InvalidOperationException($"List {listId} not found.");

        if (name is not null && string.IsNullOrWhiteSpace(name))
            throw new InvalidOperationException("name cannot be blank.");
        if (name is not null) entity.Name = name;
        if (workingDir is not null)
            entity.WorkingDir = workingDir.NullIfBlank();
        if (commitType is not null)
            entity.DefaultCommitType = commitType.NullIfBlank() ?? CommitTypeRegistry.DefaultType;

        await _lists.UpdateAsync(entity, cancellationToken);
        await _broadcaster.ListUpdated(listId);
        return ToDto(entity);
    }

    [McpServerTool, Description("Permanently delete a list and all its tasks — no undo. Only for removing the whole list, not a single task within it.")]
    public async Task<DeleteListResult> DeleteList(string listId, CancellationToken cancellationToken)
    {
        _ = await _lists.GetByIdAsync(listId, cancellationToken)
            ?? throw new InvalidOperationException($"List {listId} not found.");
        await _lists.DeleteAsync(listId, cancellationToken);
        await _broadcaster.ListUpdated(listId);
        return new DeleteListResult(true, listId);
    }

    private static ListSummaryDto ToDto(ListEntity l) =>
        new(l.Id, l.Name, l.WorkingDir, l.DefaultCommitType);
}
