"""Tests for dream() using runtime-day decay instead of calendar days."""

import pytest
import time
from unittest.mock import MagicMock, AsyncMock


@pytest.mark.asyncio
async def test_dream_uses_runtime_days_not_calendar():
    """Dream uses runtime day difference, not calendar days.

    Memory is 60 calendar days old but only 10 runtime days old.
    With calendar: energy → floor (0.05).
    With runtime + slowdown=10: energy ≈ 0.37.
    """
    from neo_cortex.cortex import CortexService

    cortex = CortexService.__new__(CortexService)
    cortex._store = MagicMock()
    cortex._index = MagicMock()
    cortex._graph = None
    cortex._embedder = None
    cortex._classifier = None
    cortex._dream_count = 0

    # current runtime day = 15, memory last accessed at rd=5 → 10 execution days
    cortex._index.get_runtime_day_count.return_value = 15
    cortex._index.get_all_for_dream.return_value = [
        {
            "id": "m1",
            "timestamp": time.time() - 86400 * 60,
            "energy": 1.0,
            "stability": 1.0,
            "last_accessed": time.time() - 86400 * 60,
            "access_count": 0,
            "created_at_rd": 1,
            "last_accessed_rd": 5,
        },
        {
            "id": "m2",
            "timestamp": time.time() - 86400 * 60,
            "energy": 1.0,
            "stability": 1.0,
            "last_accessed": time.time() - 86400 * 60,
            "access_count": 0,
            "created_at_rd": 1,
            "last_accessed_rd": 5,
        },
    ]
    cortex._store.get_all_metadata.return_value = [
        {"id": "m1", "energy": 1.0, "timestamp": time.time() - 86400 * 60},
        {"id": "m2", "energy": 1.0, "timestamp": time.time() - 86400 * 60},
    ]

    result = await cortex.dream()

    assert result.status == "completed"
    # update_energy must have been called (energy changed from 1.0)
    assert cortex._store.update_energy.called
    call_args = cortex._store.update_energy.call_args
    new_energy = call_args[0][1]
    # Runtime days: exp(-10/10/1) = exp(-1) ≈ 0.37
    # Calendar days would give exp(-60/1) = floor = 0.05
    assert new_energy > 0.3, (
        f"Expected ~0.37 (runtime days), got {new_energy:.3f} "
        "(calendar days would give 0.05)"
    )


@pytest.mark.asyncio
async def test_dream_zero_execution_days_no_decay():
    """Memory created on current runtime day → zero decay, energy unchanged."""
    from neo_cortex.cortex import CortexService

    cortex = CortexService.__new__(CortexService)
    cortex._store = MagicMock()
    cortex._index = MagicMock()
    cortex._graph = None
    cortex._embedder = None
    cortex._classifier = None
    cortex._dream_count = 0

    cortex._index.get_runtime_day_count.return_value = 5
    cortex._index.get_all_for_dream.return_value = [
        {
            "id": "m1",
            "timestamp": time.time(),
            "energy": 1.0,
            "stability": 1.0,
            "last_accessed": time.time(),
            "access_count": 0,
            "created_at_rd": 5,
            "last_accessed_rd": 5,
        },
        {
            "id": "m2",
            "timestamp": time.time(),
            "energy": 1.0,
            "stability": 1.0,
            "last_accessed": time.time(),
            "access_count": 0,
            "created_at_rd": 5,
            "last_accessed_rd": 5,
        },
    ]
    cortex._store.get_all_metadata.return_value = [
        {"id": "m1", "energy": 1.0, "timestamp": time.time()},
        {"id": "m2", "energy": 1.0, "timestamp": time.time()},
    ]

    result = await cortex.dream()

    # exp(-0/10/1) = 1.0 → no change → update_energy NOT called
    cortex._store.update_energy.assert_not_called()


@pytest.mark.asyncio
async def test_dream_high_stability_resists_decay():
    """Memory with high stability decays slowly over 30 execution days."""
    from neo_cortex.cortex import CortexService

    cortex = CortexService.__new__(CortexService)
    cortex._store = MagicMock()
    cortex._index = MagicMock()
    cortex._graph = None
    cortex._embedder = None
    cortex._classifier = None
    cortex._dream_count = 0

    # current rd=31, memory last at rd=1 → 30 execution days
    cortex._index.get_runtime_day_count.return_value = 31
    cortex._index.get_all_for_dream.return_value = [
        {
            "id": "m_stable",
            "timestamp": time.time() - 86400 * 30,
            "energy": 1.0,
            "stability": 5.0,
            "last_accessed": time.time() - 86400 * 30,
            "access_count": 40,
            "created_at_rd": 1,
            "last_accessed_rd": 1,
        },
        {
            "id": "m_other",
            "timestamp": time.time() - 86400 * 30,
            "energy": 1.0,
            "stability": 1.0,
            "last_accessed": time.time() - 86400 * 30,
            "access_count": 0,
            "created_at_rd": 1,
            "last_accessed_rd": 1,
        },
    ]
    cortex._store.get_all_metadata.return_value = [
        {"id": "m_stable", "energy": 1.0, "timestamp": time.time() - 86400 * 30},
        {"id": "m_other", "energy": 1.0, "timestamp": time.time() - 86400 * 30},
    ]

    await cortex.dream()

    # Find the call for m_stable specifically
    calls = {args[0][0]: args[0][1] for args in cortex._store.update_energy.call_args_list}
    assert "m_stable" in calls, "m_stable energy should have been updated"
    new_energy = calls["m_stable"]
    # exp(-30/10/5) = exp(-0.6) ≈ 0.55 → still above 0.5
    assert new_energy > 0.5, f"Expected >0.5 with high stability, got {new_energy:.3f}"
