from datetime import datetime, timedelta, timezone
from unittest.mock import patch

import pytest

from claude_meter.cli import (
    bar_color,
    filter_limits,
    format_reset_time,
    render_display,
    render_meter,
    sort_limits,
)


# --- bar_color ---

class TestBarColor:
    def test_green_low(self):
        assert bar_color(0) == "green"

    def test_green_under_70(self):
        assert bar_color(69.9) == "green"

    def test_yellow_at_70(self):
        assert bar_color(70) == "yellow"

    def test_yellow_under_90(self):
        assert bar_color(89.9) == "yellow"

    def test_red_at_90(self):
        assert bar_color(90) == "red"

    def test_red_at_100(self):
        assert bar_color(100) == "red"


# --- render_meter ---

class TestRenderMeter:
    def test_zero_utilization(self):
        result = render_meter(0, width=10, color=False)
        assert "[          ]" in result
        assert "0.0%" in result

    def test_full_utilization(self):
        result = render_meter(100, width=10, color=False)
        assert "[==========]" in result
        assert "100.0%" in result

    def test_half_utilization(self):
        result = render_meter(50, width=10, color=False)
        assert "====>     " in result

    def test_overflow_shows_actual_percentage(self):
        result = render_meter(127.3, width=10, color=False)
        assert "127.3%" in result
        assert "EXTRA USAGE" in result

    def test_overflow_bar_has_exclamation(self):
        result = render_meter(150, width=10, color=False)
        assert "=========!" in result

    def test_clamps_below_0(self):
        result = render_meter(-10, width=10, color=False)
        assert "0.0%" in result


# --- format_reset_time ---

class TestFormatResetTime:
    def test_none(self):
        assert format_reset_time(None) == "no reset scheduled"

    def test_empty_string(self):
        assert format_reset_time("") == "no reset scheduled"

    def test_past_time(self):
        past = (datetime.now(timezone.utc) - timedelta(hours=1)).isoformat()
        assert format_reset_time(past) == "resetting now"

    def test_hours_and_minutes(self):
        future = (datetime.now(timezone.utc) + timedelta(hours=2, minutes=30)).isoformat()
        result = format_reset_time(future)
        assert result.startswith("resets in 2h")

    def test_days(self):
        future = (datetime.now(timezone.utc) + timedelta(days=3, hours=5)).isoformat()
        result = format_reset_time(future)
        assert result.startswith("resets in 3d")

    def test_minutes_only(self):
        future = (datetime.now(timezone.utc) + timedelta(minutes=15)).isoformat()
        result = format_reset_time(future)
        assert "resets in 15m" in result or "resets in 14m" in result

    def test_invalid_string(self):
        assert format_reset_time("not-a-date") == "unknown reset time"


# --- render_display ---

class TestRenderDisplay:
    def test_empty_limits(self):
        assert render_display([]) == "No usage data available."

    def test_no_color_output(self):
        limits = [{"name": "Test", "utilization": 50.0, "resets_at": None}]
        result = render_display(limits, color=False)
        assert "Claude Meter" in result
        assert "Test" in result
        assert "50.0%" in result

    def test_color_output_contains_name(self):
        limits = [{"name": "Test", "utilization": 25.0, "resets_at": None}]
        result = render_display(limits, color=True)
        assert "Test" in result

    def test_extra_usage_enabled_shown(self):
        limits = [{"name": "Test", "utilization": 50.0, "resets_at": None}]
        result = render_display(limits, color=False, extra_usage_enabled=True)
        assert "Extra usage: enabled" in result

    def test_extra_usage_disabled_shown(self):
        limits = [{"name": "Test", "utilization": 50.0, "resets_at": None}]
        result = render_display(limits, color=False, extra_usage_enabled=False)
        assert "Extra usage: disabled" in result

    def test_extra_usage_none_not_shown(self):
        limits = [{"name": "Test", "utilization": 50.0, "resets_at": None}]
        result = render_display(limits, color=False, extra_usage_enabled=None)
        assert "Extra usage" not in result


# --- filter_limits ---

class TestFilterLimits:
    SAMPLE = [
        {"name": "7-Day Opus", "utilization": 10.0, "resets_at": None},
        {"name": "7-Day Sonnet", "utilization": 20.0, "resets_at": None},
        {"name": "5-Hour Session", "utilization": 30.0, "resets_at": None},
    ]

    def test_case_insensitive_match(self):
        result = filter_limits(self.SAMPLE, "opus")
        assert len(result) == 1
        assert result[0]["name"] == "7-Day Opus"

    def test_partial_match(self):
        result = filter_limits(self.SAMPLE, "7-Day")
        assert len(result) == 2

    def test_no_match(self):
        result = filter_limits(self.SAMPLE, "haiku")
        assert result == []


# --- sort_limits ---

class TestSortLimits:
    def test_sorts_descending(self):
        limits = [
            {"name": "A", "utilization": 10.0, "resets_at": None},
            {"name": "B", "utilization": 50.0, "resets_at": None},
            {"name": "C", "utilization": 30.0, "resets_at": None},
        ]
        result = sort_limits(limits)
        assert [l["utilization"] for l in result] == [50.0, 30.0, 10.0]

    def test_empty_list(self):
        assert sort_limits([]) == []
