import pytest
from unittest.mock import patch
from datetime import datetime, timedelta, timezone
from freezegun import freeze_time

from geminiai_cli.recommend import get_recommendation, AccountStatus

# Constants matching implementation
COOLDOWN_HOURS = 24

@pytest.fixture
def mock_data_sources():
    with patch("geminiai_cli.recommend.get_cooldown_data") as mock_cd, \
         patch("geminiai_cli.recommend.get_all_resets") as mock_resets:
        yield mock_cd, mock_resets

@freeze_time("2025-01-01 12:00:00")
def test_recommend_no_accounts(mock_data_sources):
    mock_cd, mock_resets = mock_data_sources
    mock_cd.return_value = {}
    mock_resets.return_value = []

    rec = get_recommendation()
    assert rec is None

@freeze_time("2025-01-01 12:00:00")
def test_recommend_one_ready_account(mock_data_sources):
    mock_cd, mock_resets = mock_data_sources

    # Now is 2025-01-01 12:00:00 UTC
    now = datetime(2025, 1, 1, 12, 0, 0, tzinfo=timezone.utc)

    # Account A: Used 30 hours ago (Ready)
    # Account B: Used 1 hour ago (Cooldown)
    t_ready = (now - timedelta(hours=30)).isoformat()
    t_locked = (now - timedelta(hours=1)).isoformat()

    mock_cd.return_value = {
        "ready@test.com": t_ready,
        "locked@test.com": t_locked
    }
    mock_resets.return_value = []

    rec = get_recommendation()
    assert rec is not None
    assert rec.email == "ready@test.com"
    assert rec.status == AccountStatus.READY

@freeze_time("2025-01-01 12:00:00")
def test_recommend_lru_logic(mock_data_sources):
    mock_cd, mock_resets = mock_data_sources
    now = datetime(2025, 1, 1, 12, 0, 0, tzinfo=timezone.utc)

    # Both Ready
    # Account A: Used 30 hours ago
    # Account B: Used 100 hours ago (should be preferred as "more rested")

    t_recent = (now - timedelta(hours=30)).isoformat()
    t_old = (now - timedelta(hours=100)).isoformat()

    mock_cd.return_value = {
        "recent@test.com": t_recent,
        "old@test.com": t_old
    }
    # "unused@test.com" exists in resets (known account) but not in cooldowns (never switched to)
    mock_resets.return_value = [{"email": "unused@test.com", "reset_ist": "2025-01-01T00:00:00"}]

    rec = get_recommendation()
    # Logic: Unused (Never) > Oldest Used > ...
    assert rec.email == "unused@test.com"

    # Remove unused, test between recent and old
    mock_resets.return_value = []
    rec = get_recommendation()
    assert rec.email == "old@test.com"

@freeze_time("2025-01-01 12:00:00")
def test_recommend_scheduled_logic(mock_data_sources):
    mock_cd, mock_resets = mock_data_sources
    now = datetime(2025, 1, 1, 12, 0, 0, tzinfo=timezone.utc)

    # Account A: Ready (Last used long ago)
    t_ready = (now - timedelta(hours=30)).isoformat()

    mock_cd.return_value = {
        "ready@test.com": t_ready,
        "scheduled@test.com": (now - timedelta(hours=30)).isoformat()
    }

    # Scheduled reset 1 hour in future
    future_reset = (now + timedelta(hours=1)).isoformat()
    mock_resets.return_value = [
        {"email": "scheduled@test.com", "reset_ist": future_reset}
    ]

    rec = get_recommendation()
    assert rec.email == "ready@test.com"
    assert rec.status == AccountStatus.READY

@freeze_time("2025-01-01 12:00:00")
def test_recommend_all_locked(mock_data_sources):
    mock_cd, mock_resets = mock_data_sources
    now = datetime(2025, 1, 1, 12, 0, 0, tzinfo=timezone.utc)

    # All locked
    t_locked = (now - timedelta(hours=1)).isoformat()
    mock_cd.return_value = {"locked@test.com": t_locked}
    mock_resets.return_value = []

    rec = get_recommendation()
    assert rec is None
import pytest
import json
import os
from unittest.mock import patch, MagicMock
from geminiai_cli.recommend import get_recommendation, do_recommend, AccountStatus
from geminiai_cli.config import COOLDOWN_FILE

def test_get_recommendation_no_data(fs):
    """Test when no data exists."""
    fs.create_dir(os.path.expanduser("~"))
    with patch("geminiai_cli.recommend.get_all_resets", return_value=[]):
        rec = get_recommendation()
        assert rec is None

def test_get_recommendation_all_locked(fs):
    """Test when all accounts are locked (Cooldown)."""
    fs.create_dir(os.path.expanduser("~"))
    cooldown_path = os.path.expanduser(COOLDOWN_FILE)
    now = datetime.now(timezone.utc)
    recent = (now - timedelta(hours=1)).isoformat()

    fs.create_file(cooldown_path, contents=json.dumps({"locked@test.com": recent}))

    with patch("geminiai_cli.recommend.get_all_resets", return_value=[]):
        rec = get_recommendation()
        assert rec is None

def test_get_recommendation_ready_sort_lru(fs):
    """Test picking the LRU ready account."""
    fs.create_dir(os.path.expanduser("~"))
    cooldown_path = os.path.expanduser(COOLDOWN_FILE)
    now = datetime.now(timezone.utc)

    # old1 used 10 days ago
    old1 = (now - timedelta(days=10)).isoformat()
    # old2 used 5 days ago
    old2 = (now - timedelta(days=5)).isoformat()

    fs.create_file(cooldown_path, contents=json.dumps({
        "newer@test.com": old2,
        "older@test.com": old1
    }))

    with patch("geminiai_cli.recommend.get_all_resets", return_value=[]):
        rec = get_recommendation()
        assert rec.email == "older@test.com"
        assert rec.status == AccountStatus.READY

def test_get_recommendation_never_used_first(fs):
    """Test that never used accounts come before used ones."""
    fs.create_dir(os.path.expanduser("~"))
    cooldown_path = os.path.expanduser(COOLDOWN_FILE)
    now = datetime.now(timezone.utc)
    old = (now - timedelta(days=10)).isoformat()

    # "new@test.com" is not in cooldown file, so last_used is None
    # "used@test.com" is in cooldown file
    fs.create_file(cooldown_path, contents=json.dumps({"used@test.com": old}))

    # We need to make sure "new@test.com" is known.
    # It must be in resets list or cooldown list.
    resets = [{"email": "new@test.com", "reset_ist": (now - timedelta(hours=1)).isoformat()}]

    with patch("geminiai_cli.recommend.get_all_resets", return_value=resets):
        rec = get_recommendation()
        assert rec.email == "new@test.com"

def test_get_recommendation_scheduled_ignored(fs):
    """Test that scheduled accounts (even if not recently used) are ignored if logic dictates."""
    # Logic: Status: READY > SCHEDULED > COOLDOWN.
    # Candidates with Status SCHEDULED are filtered out in step 3 (only READY kept).

    fs.create_dir(os.path.expanduser("~"))
    now = datetime.now(timezone.utc)
    future = (now + timedelta(hours=10)).isoformat()

    # This email has a future reset, so it should be SCHEDULED
    resets = [{"email": "scheduled@test.com", "reset_ist": future}]

    with patch("geminiai_cli.recommend.get_all_resets", return_value=resets):
        rec = get_recommendation()
        assert rec is None

def test_do_recommend_success(fs, capsys):
    """Test CLI output for successful recommendation."""
    rec = MagicMock()
    rec.email = "best@test.com"
    rec.last_used = datetime.now(timezone.utc) - timedelta(days=2)

    # Patch colors to be valid rich styles or empty
    with patch("geminiai_cli.recommend.NEON_GREEN", "green"), \
         patch("geminiai_cli.recommend.NEON_RED", "red"), \
         patch("geminiai_cli.recommend.get_recommendation", return_value=rec):
        do_recommend()

    captured = capsys.readouterr()
    assert "best@test.com" in captured.out
    assert "2d" in captured.out
    assert "Account is Ready" in captured.out

def test_do_recommend_none(fs, capsys):
    """Test CLI output when no recommendation found."""
    with patch("geminiai_cli.recommend.NEON_GREEN", "green"), \
         patch("geminiai_cli.recommend.NEON_RED", "red"), \
         patch("geminiai_cli.recommend.get_recommendation", return_value=None):
        do_recommend()

    captured = capsys.readouterr()
    assert "No 'Green' (Ready) accounts" in captured.out

def test_do_recommend_never_used(fs, capsys):
    """Test CLI output for never used account."""
    rec = MagicMock()
    rec.email = "fresh@test.com"
    rec.last_used = None

    with patch("geminiai_cli.recommend.NEON_GREEN", "green"), \
         patch("geminiai_cli.recommend.NEON_RED", "red"), \
         patch("geminiai_cli.recommend.get_recommendation", return_value=rec):
        do_recommend()

    captured = capsys.readouterr()
    assert "fresh@test.com" in captured.out
    assert "Never / Unknown" in captured.out
