from __future__ import annotations

import math
import unittest
from datetime import date, datetime, timedelta
from decimal import Decimal

from aniket_tools.responses import PandasTimedelta, value_correction


class TestValueCorrection(unittest.TestCase):
    def test_default_response_mode_matches_project_style(self) -> None:
        payload = {
            "name": "  aniket  ",
            "price": Decimal("10.50"),
            "created_at": datetime(2024, 1, 2, 3, 4, 5),
            "created_on": date(2024, 1, 2),
            "duration": timedelta(hours=2, minutes=30),
            "score": 12.3456,
        }

        corrected = value_correction(payload)

        self.assertEqual(corrected["name"], "aniket")
        self.assertEqual(corrected["price"], 10.5)
        self.assertEqual(corrected["created_at"], "2024-01-02 03:04:05")
        self.assertEqual(corrected["created_on"], "2024-01-02")
        self.assertEqual(corrected["duration"], "2:30:00")
        self.assertEqual(corrected["score"], 12.35)

    def test_datetime_and_date_can_be_preserved(self) -> None:
        current_datetime = datetime(2024, 1, 2, 3, 4, 5)
        current_date = date(2024, 1, 2)

        self.assertIs(
            value_correction(current_datetime, convert_datetime=False),
            current_datetime,
        )
        self.assertIs(
            value_correction(current_date, convert_date=False),
            current_date,
        )

    def test_float_precision_can_be_changed_or_disabled(self) -> None:
        self.assertEqual(value_correction(12.3456, float_precision=3), 12.346)
        self.assertEqual(value_correction(12.3456, float_precision=None), 12.3456)

    def test_nan_and_inf_become_none_in_response_mode(self) -> None:
        self.assertIsNone(value_correction(float("nan")))
        self.assertIsNone(value_correction(float("inf")))
        self.assertTrue(math.isnan(value_correction(float("nan"), nan_to_none=False)))

    def test_nested_values_follow_same_options(self) -> None:
        payload = {
            "created_at": datetime(2024, 1, 2, 3, 4, 5),
            "items": [12.3456, {"due_on": date(2024, 1, 3)}],
        }

        corrected = value_correction(
            payload,
            convert_datetime=False,
            convert_date=False,
            float_precision=1,
        )

        self.assertIsInstance(corrected["created_at"], datetime)
        self.assertEqual(corrected["items"][0], 12.3)
        self.assertIsInstance(corrected["items"][1]["due_on"], date)

    def test_preserve_mode_keeps_date_like_values_unconverted(self) -> None:
        current_datetime = datetime(2024, 1, 2, 3, 4, 5)
        current_date = date(2024, 1, 2)
        current_delta = timedelta(minutes=15)

        self.assertIs(value_correction(current_datetime, mode="preserve"), current_datetime)
        self.assertIs(value_correction(current_date, mode="preserve"), current_date)
        self.assertIs(value_correction(current_delta, mode="preserve"), current_delta)
        self.assertEqual(value_correction(1.2345, mode="preserve"), 1.2345)

    def test_tuple_and_frozenset_are_supported(self) -> None:
        corrected_tuple = value_correction((Decimal("1.5"), datetime(2024, 1, 2, 3, 4, 5)))
        corrected_frozenset = value_correction(frozenset({Decimal("1.25"), Decimal("2.50")}))

        self.assertEqual(corrected_tuple, (1.5, "2024-01-02 03:04:05"))
        self.assertEqual(corrected_frozenset, [1.25, 2.5])

    @unittest.skipIf(PandasTimedelta is None, "pandas is not installed")
    def test_pandas_timedelta_is_supported_when_available(self) -> None:
        pandas_delta = PandasTimedelta(days=1, minutes=5)
        self.assertEqual(value_correction(pandas_delta), "1 days 00:05:00")


if __name__ == "__main__":
    unittest.main()
