"""Unit tests for libs/backtest/calendar.py."""
from __future__ import annotations

import datetime as dt

import pytest

from libs.backtest.calendar import (
    get_trading_days,
    is_trading_day,
    next_trading_day,
    resolve_execution_date,
)


class TestResolvExecutionDate:
    def test_pre_market_weekday(self):
        # pre_market on a trading day: reaction = same day, execution = next trading day
        event_date = dt.date(2026, 1, 5)  # Monday
        exec_date = resolve_execution_date(event_date, "pre_market")
        assert is_trading_day(exec_date)
        assert exec_date > event_date

    def test_post_market_weekday(self):
        # post_market: reaction = next trading day, execution = trading day after that
        event_date = dt.date(2026, 1, 5)  # Monday
        exec_date = resolve_execution_date(event_date, "post_market")
        assert is_trading_day(exec_date)
        assert exec_date > event_date

    def test_post_market_friday(self):
        # post_market Friday → reaction = Monday, execution = Tuesday
        friday = dt.date(2026, 1, 2)  # Friday
        exec_date = resolve_execution_date(friday, "post_market")
        assert is_trading_day(exec_date)
        # Must be at least Monday
        assert exec_date >= dt.date(2026, 1, 5)

    def test_unknown_bucket(self):
        # unknown treated same as post_market
        event_date = dt.date(2026, 1, 5)
        exec_date = resolve_execution_date(event_date, "unknown")
        assert is_trading_day(exec_date)
        assert exec_date > event_date

    def test_execution_after_reaction(self):
        """execution_date should always be strictly after event_date."""
        for bucket in ["pre_market", "regular_hours", "post_market", "unknown"]:
            exec_date = resolve_execution_date(dt.date(2026, 1, 5), bucket)
            assert exec_date > dt.date(2026, 1, 5), f"Failed for bucket: {bucket}"


class TestGetTradingDays:
    def test_basic_range(self):
        days = get_trading_days(dt.date(2026, 1, 5), dt.date(2026, 1, 9))
        assert len(days) == 5  # Mon-Fri
        assert all(is_trading_day(d) for d in days)

    def test_excludes_weekends(self):
        days = get_trading_days(dt.date(2026, 1, 3), dt.date(2026, 1, 11))
        for d in days:
            assert d.weekday() < 5  # Not Saturday (5) or Sunday (6)

    def test_single_day(self):
        days = get_trading_days(dt.date(2026, 1, 5), dt.date(2026, 1, 5))
        assert len(days) == 1
        assert days[0] == dt.date(2026, 1, 5)

    def test_sorted_ascending(self):
        days = get_trading_days(dt.date(2026, 1, 5), dt.date(2026, 1, 30))
        assert days == sorted(days)


class TestIsTradingDay:
    def test_weekday_is_trading(self):
        assert is_trading_day(dt.date(2026, 1, 5))  # Monday

    def test_weekend_not_trading(self):
        assert not is_trading_day(dt.date(2026, 1, 3))  # Saturday

    def test_sunday_not_trading(self):
        assert not is_trading_day(dt.date(2026, 1, 4))  # Sunday


class TestNextTradingDay:
    def test_friday_to_monday(self):
        friday = dt.date(2026, 1, 2)
        nxt = next_trading_day(friday)
        assert nxt == dt.date(2026, 1, 5)  # Monday

    def test_monday_to_tuesday(self):
        monday = dt.date(2026, 1, 5)
        nxt = next_trading_day(monday)
        assert nxt == dt.date(2026, 1, 6)
