"""Garbage collection tool - remove or archive low-scoring memories."""

import time
from typing import Any

from ..config import get_config
from ..context import db, mcp
from ..core.scoring import should_forget
from ..security.validators import validate_positive_int
from ..storage.models import GarbageCollectionResult, MemoryStatus


@mcp.tool()
def gc(
    dry_run: bool = True,
    archive_instead: bool = False,
    limit: int | None = None,
) -> dict[str, Any]:
    """
    Perform garbage collection on low-scoring memories.

    Removes or archives memories whose decay score has fallen below the
    forget threshold. This prevents the database from growing indefinitely
    with unused memories.

    Args:
        dry_run: Preview what would be removed without actually removing.
        archive_instead: Archive memories instead of deleting.
        limit: Maximum number of memories to process (1-10,000).

    Returns:
        Statistics about removed/archived memories.

    Raises:
        ValueError: If limit is out of valid range.
    """
    # Input validation
    if limit is not None:
        limit = validate_positive_int(limit, "limit", min_value=1, max_value=10000)

    config = get_config()
    now = int(time.time())

    memories = db.list_memories(status=MemoryStatus.ACTIVE)

    to_remove = []
    total_score_removed = 0.0
    for memory in memories:
        should_delete, score = should_forget(memory, now)
        if should_delete:
            to_remove.append((memory, score))
            total_score_removed += score

    to_remove.sort(key=lambda x: x[1])

    if limit and len(to_remove) > limit:
        to_remove = to_remove[:limit]
        total_score_removed = sum(score for _, score in to_remove)

    removed_count = 0
    archived_count = 0
    memory_ids = []

    if not dry_run:
        for memory, _score in to_remove:
            memory_ids.append(memory.id)
            if archive_instead:
                db.update_memory(memory_id=memory.id, status=MemoryStatus.ARCHIVED)
                archived_count += 1
            else:
                db.delete_memory(memory.id)
                removed_count += 1
    else:
        memory_ids = [memory.id for memory, _ in to_remove]
        if archive_instead:
            archived_count = len(to_remove)
        else:
            removed_count = len(to_remove)

    result = GarbageCollectionResult(
        removed_count=removed_count,
        archived_count=archived_count,
        freed_score_sum=total_score_removed,
        memory_ids=memory_ids,
    )

    return {
        "success": True,
        "dry_run": dry_run,
        "removed_count": result.removed_count,
        "archived_count": result.archived_count,
        "freed_score_sum": round(result.freed_score_sum, 4),
        "memory_ids": result.memory_ids[:10],
        "total_affected": len(result.memory_ids),
        "message": (
            f"{'Would remove' if dry_run else 'Removed'} {len(result.memory_ids)} "
            f"low-scoring memories (threshold: {config.forget_threshold})"
        ),
    }
