"""
Test utility functions in server module.
"""

from datetime import date, datetime
from decimal import Decimal
from unittest.mock import patch

import pytest

import server


def test_decimal_precision_milliunits_conversion() -> None:
    """Test that milliunits conversion maintains Decimal precision."""
    # Test various milliunits values that could lose precision with floats
    test_cases = [
        (123456, Decimal("123.456")),  # Regular amount
        (1, Decimal("0.001")),  # Smallest unit
        (999, Decimal("0.999")),  # Just under 1
        (1000, Decimal("1")),  # Exactly 1
        (1001, Decimal("1.001")),  # Just over 1
        (999999999, Decimal("999999.999")),  # Large amount
        (-50000, Decimal("-50")),  # Negative amount
        (0, Decimal("0")),  # Zero
    ]

    for milliunits, expected in test_cases:
        from models import milliunits_to_currency

        result = milliunits_to_currency(milliunits)
        assert result == expected, (
            f"Failed for {milliunits}: got {result}, expected {expected}"
        )
        # Ensure result is actually a Decimal, not float
        assert isinstance(result, Decimal), (
            f"Result {result} is not a Decimal but {type(result)}"
        )


def test_milliunits_to_currency_valid_input() -> None:
    """Test milliunits conversion with valid input."""
    from models import milliunits_to_currency

    result = milliunits_to_currency(123456)
    assert result == Decimal("123.456")


def test_milliunits_to_currency_none_input() -> None:
    """Test milliunits conversion with None input raises TypeError."""
    from typing import Any

    with pytest.raises(TypeError):
        from models import milliunits_to_currency

        none_value: Any = None
        milliunits_to_currency(none_value)


def test_milliunits_to_currency_zero() -> None:
    """Test milliunits conversion with zero."""
    from models import milliunits_to_currency

    result = milliunits_to_currency(0)
    assert result == Decimal("0")


def test_milliunits_to_currency_negative() -> None:
    """Test milliunits conversion with negative value."""
    from models import milliunits_to_currency

    result = milliunits_to_currency(-50000)
    assert result == Decimal("-50")


def test_convert_month_to_date_with_date_object() -> None:
    """Test convert_month_to_date with date object returns unchanged."""
    test_date = date(2024, 3, 15)
    result = server.convert_month_to_date(test_date)
    assert result == test_date


def test_convert_month_to_date_with_current() -> None:
    """Test convert_month_to_date with 'current' returns current month date."""
    with patch("server.datetime") as mock_datetime:
        mock_datetime.now.return_value = datetime(2024, 9, 20, 16, 45, 0)

        result = server.convert_month_to_date("current")
        assert result == date(2024, 9, 1)


def test_convert_month_to_date_with_last_and_next() -> None:
    """Test convert_month_to_date with 'last' and 'next' literals."""
    # Test normal month (June -> May and July)
    with patch("server.datetime") as mock_datetime:
        mock_datetime.now.return_value = datetime(2024, 6, 15, 10, 30, 0)

        result_last = server.convert_month_to_date("last")
        assert result_last == date(2024, 5, 1)

        result_next = server.convert_month_to_date("next")
        assert result_next == date(2024, 7, 1)

    # Test January edge case (January -> December previous year)
    with patch("server.datetime") as mock_datetime:
        mock_datetime.now.return_value = datetime(2024, 1, 10, 14, 45, 0)

        result_last = server.convert_month_to_date("last")
        assert result_last == date(2023, 12, 1)

        result_next = server.convert_month_to_date("next")
        assert result_next == date(2024, 2, 1)

    # Test December edge case (December -> January next year)
    with patch("server.datetime") as mock_datetime:
        mock_datetime.now.return_value = datetime(2024, 12, 25, 9, 15, 0)

        result_last = server.convert_month_to_date("last")
        assert result_last == date(2024, 11, 1)

        result_next = server.convert_month_to_date("next")
        assert result_next == date(2025, 1, 1)


def test_convert_month_to_date_invalid_value() -> None:
    """Test convert_month_to_date with invalid value raises error."""
    from typing import Any

    with pytest.raises(ValueError, match="Invalid month value: invalid"):
        invalid_value: Any = "invalid"
        server.convert_month_to_date(invalid_value)


def test_convert_transaction_to_model_basic() -> None:
    """Test Transaction.from_ynab with basic transaction."""
    import ynab

    from models import Transaction

    txn = ynab.TransactionDetail(
        id="txn-123",
        date=date(2024, 6, 15),
        amount=-50000,
        memo="Test transaction",
        cleared=ynab.TransactionClearedStatus.CLEARED,
        approved=True,
        flag_color=ynab.TransactionFlagColor.RED,
        account_id="acc-1",
        payee_id="payee-1",
        category_id="cat-1",
        transfer_account_id=None,
        transfer_transaction_id=None,
        matched_transaction_id=None,
        import_id=None,
        import_payee_name=None,
        import_payee_name_original=None,
        debt_transaction_type=None,
        deleted=False,
        account_name="Checking",
        payee_name="Test Payee",
        category_name="Test Category",
        subtransactions=[],
    )

    result = Transaction.from_ynab(txn)

    assert result.id == "txn-123"
    assert result.date == date(2024, 6, 15)
    assert result.amount == Decimal("-50")
    assert result.account_name == "Checking"
    assert result.payee_name == "Test Payee"
    assert result.category_name == "Test Category"
    assert result.subtransactions is None


def test_convert_transaction_to_model_without_optional_attributes() -> None:
    """Test Transaction.from_ynab with minimal TransactionDetail."""
    import ynab

    from models import Transaction

    minimal_txn = ynab.TransactionDetail(
        id="txn-456",
        date=date(2024, 6, 16),
        amount=-25000,
        memo="Minimal transaction",
        cleared=ynab.TransactionClearedStatus.UNCLEARED,
        approved=True,
        flag_color=None,
        account_id="acc-2",
        payee_id="payee-2",
        category_id="cat-2",
        transfer_account_id=None,
        transfer_transaction_id=None,
        matched_transaction_id=None,
        import_id=None,
        import_payee_name=None,
        import_payee_name_original=None,
        debt_transaction_type=None,
        deleted=False,
        account_name="Test Account 2",
        payee_name="Test Payee 2",
        category_name="Test Category 2",
        subtransactions=[],
    )

    result = Transaction.from_ynab(minimal_txn)

    assert result.id == "txn-456"
    assert result.account_name == "Test Account 2"
    assert result.payee_name == "Test Payee 2"
    assert result.category_name == "Test Category 2"


def test_milliunits_to_currency_from_models() -> None:
    """Test milliunits_to_currency function from models module."""
    from models import milliunits_to_currency

    assert milliunits_to_currency(50000) == Decimal("50")
    assert milliunits_to_currency(-25000) == Decimal("-25")
    assert milliunits_to_currency(1000) == Decimal("1")
    assert milliunits_to_currency(0) == Decimal("0")
