using ClaudeDo.Data.Models;
using ClaudeDo.Data.Repositories;

namespace ClaudeDo.Worker.Findings;

/// <summary>The main checkout a finding belongs to, plus whether its store is committed with the repo.</summary>
public sealed record FindingsTarget(string ListId, string ListName, string WorkingDir, bool Tracked);

/// <summary>
/// Maps an MCP call to the project whose findings store it targets. Always resolves to the list's
/// WorkingDir — the main checkout — even when the caller runs inside a worktree, because concurrent
/// writes into worktree copies would produce INDEX.md merge conflicts.
/// </summary>
public sealed class FindingsStoreLocator
{
    private readonly TaskRepository _tasks;
    private readonly ListRepository _lists;

    public FindingsStoreLocator(TaskRepository tasks, ListRepository lists)
    {
        _tasks = tasks;
        _lists = lists;
    }

    public async Task<FindingsTarget> ResolveForTaskAsync(string taskId, CancellationToken ct)
    {
        var task = await _tasks.GetByIdAsync(taskId, ct)
            ?? throw new InvalidOperationException($"Task {taskId} not found.");
        var list = await _lists.GetByIdAsync(task.ListId, ct)
            ?? throw new InvalidOperationException($"List {task.ListId} not found.");
        return ToTarget(list);
    }

    public async Task<FindingsTarget> ResolveForListAsync(string listIdOrName, CancellationToken ct)
    {
        var all = await _lists.GetAllAsync(ct);

        if (!string.IsNullOrWhiteSpace(listIdOrName))
        {
            var match = all.FirstOrDefault(l => l.Id == listIdOrName)
                ?? all.FirstOrDefault(l => string.Equals(l.Name, listIdOrName, StringComparison.OrdinalIgnoreCase))
                ?? throw new InvalidOperationException(
                    $"No list matches '{listIdOrName}'. Known lists: {Names(all)}.");
            return ToTarget(match);
        }

        var withDir = all.Where(l => !string.IsNullOrWhiteSpace(l.WorkingDir)).ToList();
        return withDir.Count switch
        {
            1 => ToTarget(withDir[0]),
            0 => throw new InvalidOperationException("No list has a working directory, so there is nowhere to save a finding."),
            _ => throw new InvalidOperationException(
                $"Several lists have a working directory — pass 'list' to pick one: {Names(withDir)}."),
        };
    }

    private static string Names(IEnumerable<ListEntity> lists) => string.Join(", ", lists.Select(l => l.Name));

    private static FindingsTarget ToTarget(ListEntity list)
    {
        if (string.IsNullOrWhiteSpace(list.WorkingDir))
            throw new InvalidOperationException($"List '{list.Name}' has no working directory, so it has no findings store.");
        return new FindingsTarget(list.Id, list.Name, list.WorkingDir, list.FindingsTracked);
    }
}
