using ClaudeDo.Data.Models;
using Microsoft.EntityFrameworkCore;

namespace ClaudeDo.Data.Repositories;

public sealed class ListRepository
{
    private readonly ClaudeDoDbContext _context;
    private readonly AttachmentStore _attachments;

    public ListRepository(ClaudeDoDbContext context, AttachmentStore? attachments = null)
    {
        _context = context;
        _attachments = attachments ?? new AttachmentStore();
    }

    public async Task AddAsync(ListEntity entity, CancellationToken ct = default)
    {
        NormalizeWorkingDir(entity);
        _context.Lists.Add(entity);
        await _context.SaveChangesAsync(ct);
    }

    public async Task UpdateAsync(ListEntity entity, CancellationToken ct = default)
    {
        NormalizeWorkingDir(entity);
        _context.Lists.Update(entity);
        await _context.SaveChangesAsync(ct);
    }

    // A working dir is user-supplied (typed, folder-picked, imported, or set over MCP) and every
    // consumer eventually hands it to the claude CLI or wt.exe as one quoted command-line token —
    // where a trailing '\' escapes its own closing quote. Normalizing here covers all writers;
    // see Paths.TrimTrailingSeparator.
    private static void NormalizeWorkingDir(ListEntity entity) =>
        entity.WorkingDir = Paths.TrimTrailingSeparator(entity.WorkingDir);

    public async Task DeleteAsync(string listId, CancellationToken ct = default)
    {
        var taskIds = await _context.Tasks
            .Where(t => t.ListId == listId)
            .Select(t => t.Id)
            .ToListAsync(ct);
        await _context.Lists.Where(l => l.Id == listId).ExecuteDeleteAsync(ct);
        foreach (var id in taskIds)
            _attachments.DeleteTaskDir(id);
    }

    public async Task<ListEntity?> GetByIdAsync(string listId, CancellationToken ct = default)
    {
        return await _context.Lists.FirstOrDefaultAsync(l => l.Id == listId, ct);
    }

    public async Task<List<ListEntity>> GetAllAsync(CancellationToken ct = default)
    {
        return await _context.Lists.OrderBy(l => l.SortOrder).ThenBy(l => l.CreatedAt).ToListAsync(ct);
    }

    public async Task ReorderAsync(IReadOnlyList<string> orderedListIds, CancellationToken ct = default)
    {
        var idSet = orderedListIds.ToHashSet();
        var entities = await _context.Lists.Where(l => idSet.Contains(l.Id)).ToListAsync(ct);
        for (int i = 0; i < orderedListIds.Count; i++)
        {
            var e = entities.FirstOrDefault(x => x.Id == orderedListIds[i]);
            if (e is not null) e.SortOrder = i;
        }
        await _context.SaveChangesAsync(ct);
    }

    public async Task<ListConfigEntity?> GetConfigAsync(string listId, CancellationToken ct = default)
    {
        return await _context.ListConfigs.FirstOrDefaultAsync(c => c.ListId == listId, ct);
    }

    public async Task SetConfigAsync(ListConfigEntity config, CancellationToken ct = default)
    {
        var existing = await _context.ListConfigs.FirstOrDefaultAsync(c => c.ListId == config.ListId, ct);
        if (existing is null)
        {
            _context.ListConfigs.Add(config);
        }
        else
        {
            existing.Model = config.Model;
            existing.SystemPrompt = config.SystemPrompt;
            existing.AgentPath = config.AgentPath;
            existing.MaxTurns = config.MaxTurns;
            existing.SessionSkills = config.SessionSkills;
            existing.VerifyCommand = config.VerifyCommand;
            existing.PermissionMode = config.PermissionMode;
            existing.TicketProjectId = config.TicketProjectId;
        }
        await _context.SaveChangesAsync(ct);
    }

    public async Task<bool> DeleteConfigAsync(string listId, CancellationToken ct = default)
    {
        var affected = await _context.ListConfigs
            .Where(c => c.ListId == listId)
            .ExecuteDeleteAsync(ct);
        return affected > 0;
    }
}
