namespace ClawdDotNet.Core.Memory;

/// <summary>
/// Wem eine Erinnerung gehört.
/// Bewusst dieselbe Trennung wie beim FileRW-Tool, damit sie für Agenten
/// nachvollziehbar bleibt.
/// </summary>
public enum MemoryScope
{
    /// <summary>Nur für den Agenten selbst sichtbar.</summary>
    Agent,

    /// <summary>Für alle Agenten der Instanz sichtbar.</summary>
    Shared
}

/// <summary>Grobe Einordnung, damit Recall gezielt filtern kann.</summary>
public static class MemoryCategory
{
    public const string Fact = "fact";                 // gesicherte Angabe
    public const string Decision = "decision";         // getroffene Entscheidung
    public const string Observation = "observation";   // Beobachtung, Zwischenstand
    public const string Task = "task";                 // offener Punkt
    public const string Contact = "contact";           // Person, Kanal, Zugang
    public const string Other = "other";

    public static readonly string[] All =
        [Fact, Decision, Observation, Task, Contact, Other];

    public static string Normalize(string? value)
    {
        var v = value?.Trim().ToLowerInvariant();
        return All.Contains(v) ? v! : Other;
    }
}

public sealed record MemoryEntry
{
    public long Id { get; init; }
    public MemoryScope Scope { get; init; }

    /// <summary>Agent-Id bei <see cref="MemoryScope.Agent"/>, sonst leer.</summary>
    public string OwnerId { get; init; } = "";

    public string Category { get; init; } = MemoryCategory.Other;

    /// <summary>Worum es geht — etwa ein Ticker, ein Kundenname, ein Projekt.</summary>
    public string Subject { get; init; } = "";

    /// <summary>
    /// Optionaler eindeutiger Schlüssel. Erneutes Merken unter demselben Schlüssel
    /// aktualisiert die Erinnerung, statt eine zweite anzulegen — so bleibt der
    /// Bestand über Monate hinweg brauchbar statt zuzuwachsen.
    /// </summary>
    public string? Key { get; init; }

    public string Content { get; init; } = "";

    public IReadOnlyList<string> Tags { get; init; } = [];

    /// <summary>1 (nebensächlich) bis 5 (zentral). Steuert die Reihenfolge beim Abruf.</summary>
    public int Importance { get; init; } = 3;

    public DateTime CreatedAt { get; init; }
    public DateTime UpdatedAt { get; init; }

    /// <summary>Welcher Agent die Erinnerung angelegt hat — auch bei geteiltem Scope.</summary>
    public string CreatedBy { get; init; } = "";
}

/// <summary>Suchkriterien für den Abruf.</summary>
public sealed record MemoryQuery
{
    public MemoryScope Scope { get; init; } = MemoryScope.Agent;
    public string OwnerId { get; init; } = "";

    /// <summary>Freitext — wird gegen Betreff, Inhalt und Schlagworte geprüft.</summary>
    public string? Search { get; init; }

    public string? Subject { get; init; }
    public string? Category { get; init; }
    public IReadOnlyList<string> Tags { get; init; } = [];

    public int Limit { get; init; } = 20;

    /// <summary>Nur Erinnerungen ab dieser Wichtigkeit.</summary>
    public int MinImportance { get; init; } = 1;
}

public interface IMemoryRepository
{
    /// <summary>
    /// Legt eine Erinnerung an oder aktualisiert sie, wenn ein Schlüssel angegeben ist
    /// und darunter bereits eine besteht.
    /// </summary>
    Task<MemoryEntry> RememberAsync(MemoryEntry entry, CancellationToken ct);

    Task<IReadOnlyList<MemoryEntry>> RecallAsync(MemoryQuery query, CancellationToken ct);

    Task<MemoryEntry?> GetByKeyAsync(MemoryScope scope, string ownerId, string key, CancellationToken ct);

    /// <summary>Löscht eine Erinnerung. Gibt an, ob es etwas zu löschen gab.</summary>
    Task<bool> ForgetAsync(long id, CancellationToken ct);

    Task<int> ForgetBySubjectAsync(MemoryScope scope, string ownerId, string subject, CancellationToken ct);

    /// <summary>Betreffs mit Anzahl — für einen Überblick, ohne alle Inhalte zu laden.</summary>
    Task<IReadOnlyList<(string Subject, int Count)>> ListSubjectsAsync(
        MemoryScope scope, string ownerId, int limit, CancellationToken ct);

    Task<int> CountAsync(MemoryScope scope, string ownerId, CancellationToken ct);
}
