#!/usr/bin/env python3
"""
真太阳时计算测试
"""

from datetime import datetime

import pytest

from core.birth_info import BirthInfo
from utils.solar_time import (
    adjust_time_index_for_solar_time,
    beijing_to_solar_time,
    calculate_solar_time_offset,
    calculate_time_index,
    get_longitude_by_city,
    get_major_cities_longitude,
)


class TestSolarTimeOffset:
    """真太阳时偏移量计算测试"""

    def test_beijing_offset(self):
        """测试北京的时差"""
        offset = calculate_solar_time_offset(116.4)
        assert offset == -14  # 比北京时间慢14分钟

    def test_shanghai_offset(self):
        """测试上海的时差"""
        offset = calculate_solar_time_offset(121.5)
        assert offset == 6  # 比北京时间快6分钟

    def test_urumqi_offset(self):
        """测试乌鲁木齐的时差"""
        offset = calculate_solar_time_offset(87.6)
        assert offset == -130  # 比北京时间慢约130分钟

    def test_guangzhou_offset(self):
        """测试广州的时差"""
        offset = calculate_solar_time_offset(113.3)
        assert offset == -27  # 比北京时间慢27分钟

    def test_standard_longitude(self):
        """测试标准经度（120°）的时差"""
        offset = calculate_solar_time_offset(120.0)
        assert offset == 0  # 无时差


class TestBeijingToSolarTime:
    """北京时间转真太阳时测试"""

    def test_beijing_conversion(self):
        """测试北京时间转换"""
        bt = datetime(2000, 8, 16, 12, 0)
        st = beijing_to_solar_time(bt, 116.4)
        assert st.hour == 11
        assert st.minute == 46

    def test_shanghai_conversion(self):
        """测试上海时间转换"""
        bt = datetime(2000, 8, 16, 12, 0)
        st = beijing_to_solar_time(bt, 121.5)
        assert st.hour == 12
        assert st.minute == 6

    def test_urumqi_conversion(self):
        """测试乌鲁木齐时间转换"""
        bt = datetime(2000, 8, 16, 12, 0)
        st = beijing_to_solar_time(bt, 87.6)
        assert st.hour == 9
        assert st.minute == 50


class TestTimeIndexCalculation:
    """时辰序号计算测试"""

    def test_zi_shi_early(self):
        """测试早子时"""
        assert calculate_time_index(0, 0) == 0
        assert calculate_time_index(0, 59) == 0

    def test_zi_shi_late(self):
        """测试晚子时"""
        assert calculate_time_index(23, 0) == 12
        assert calculate_time_index(23, 59) == 12

    def test_chou_shi(self):
        """测试丑时"""
        assert calculate_time_index(1, 0) == 1
        assert calculate_time_index(2, 59) == 1

    def test_wu_shi(self):
        """测试午时"""
        assert calculate_time_index(11, 0) == 6
        assert calculate_time_index(12, 59) == 6

    def test_you_shi(self):
        """测试酉时"""
        assert calculate_time_index(17, 0) == 9
        assert calculate_time_index(18, 59) == 9


class TestAdjustTimeIndexForSolarTime:
    """真太阳时时辰修正测试"""

    def test_beijing_no_change(self):
        """测试北京时辰不变"""
        # 北京12:00，午时，真太阳时11:46仍是午时
        index, hour, minute = adjust_time_index_for_solar_time(12, 0, 116.4)
        assert index == 6  # 午时
        assert hour == 11
        assert minute == 46

    def test_urumqi_time_change(self):
        """测试乌鲁木齐时辰改变"""
        # 乌鲁木齐12:00，原本午时，真太阳时9:50变为巳时
        index, hour, minute = adjust_time_index_for_solar_time(12, 0, 87.6)
        assert index == 5  # 巳时
        assert hour == 9
        assert minute == 50

    def test_boundary_case_1(self):
        """测试时辰边界情况1"""
        # 北京11:00，原本午时，真太阳时10:46仍是巳时
        index, hour, minute = adjust_time_index_for_solar_time(11, 0, 116.4)
        assert index == 5  # 巳时（10:46）

    def test_boundary_case_2(self):
        """测试时辰边界情况2"""
        # 上海11:00，原本午时，真太阳时11:06仍是午时
        index, hour, minute = adjust_time_index_for_solar_time(11, 0, 121.5)
        assert index == 6  # 午时（11:06）


class TestCityLongitude:
    """城市经度查询测试"""

    def test_major_cities_exist(self):
        """测试主要城市都存在"""
        cities = get_major_cities_longitude()
        assert "北京" in cities
        assert "上海" in cities
        assert "广州" in cities
        assert "深圳" in cities

    def test_get_longitude_beijing(self):
        """测试获取北京经度"""
        longitude = get_longitude_by_city("北京")
        assert longitude == 116.4

    def test_get_longitude_urumqi(self):
        """测试获取乌鲁木齐经度"""
        longitude = get_longitude_by_city("乌鲁木齐")
        assert longitude == 87.6

    def test_invalid_city(self):
        """测试无效城市名"""
        with pytest.raises(ValueError) as exc_info:
            get_longitude_by_city("不存在的城市")
        assert "不在数据库中" in str(exc_info.value)


class TestBirthInfoSolarTime:
    """BirthInfo真太阳时集成测试"""

    def test_normal_mode_no_correction(self):
        """测试普通模式不修正"""
        info = BirthInfo(date="2000-08-16", time_index=6, gender="女")
        assert info.get_adjusted_time_index() == 6

    def test_solar_time_enabled_beijing(self):
        """测试真太阳时模式（北京）"""
        info = BirthInfo(
            date="2000-08-16",
            time_index=6,
            gender="女",
            longitude=116.4,
            use_solar_time=True,
            birth_hour=12,
            birth_minute=0,
        )
        # 北京12:00午时，真太阳时11:46仍是午时
        assert info.get_adjusted_time_index() == 6

    def test_solar_time_enabled_urumqi(self):
        """测试真太阳时模式（乌鲁木齐）"""
        info = BirthInfo(
            date="2000-08-16",
            time_index=6,
            gender="女",
            longitude=87.6,
            use_solar_time=True,
            birth_hour=12,
            birth_minute=0,
        )
        # 乌鲁木齐12:00午时，真太阳时9:50变为巳时
        assert info.get_adjusted_time_index() == 5

    def test_solar_time_validation_no_longitude(self):
        """测试启用真太阳时但未提供经度"""
        with pytest.raises(ValueError) as exc_info:
            BirthInfo(
                date="2000-08-16",
                time_index=6,
                gender="女",
                use_solar_time=True,  # 启用但未提供经度
            )
        assert "经度" in str(exc_info.value)

    def test_solar_time_info_string(self):
        """测试真太阳时信息字符串"""
        info = BirthInfo(
            date="2000-08-16",
            time_index=6,
            gender="女",
            longitude=116.4,
            use_solar_time=True,
            birth_hour=12,
            birth_minute=0,
        )
        info_str = info.get_solar_time_info()
        assert info_str is not None
        assert "北京时间" in info_str
        assert "真太阳时" in info_str
        assert "116.4°E" in info_str

    def test_solar_time_info_none_when_disabled(self):
        """测试禁用真太阳时时信息为None"""
        info = BirthInfo(date="2000-08-16", time_index=6, gender="女")
        assert info.get_solar_time_info() is None

    def test_to_dict_with_solar_time(self):
        """测试包含真太阳时的字典转换"""
        info = BirthInfo(
            date="2000-08-16",
            time_index=6,
            gender="女",
            longitude=116.4,
            latitude=39.9,
            use_solar_time=True,
            birth_hour=12,
            birth_minute=0,
        )
        data = info.to_dict()
        assert data["longitude"] == 116.4
        assert data["latitude"] == 39.9
        assert data["use_solar_time"] is True
        assert data["birth_hour"] == 12
        assert data["birth_minute"] == 0

    def test_from_dict_with_solar_time(self):
        """测试从字典创建包含真太阳时的对象"""
        data = {
            "date": "2000-08-16",
            "time_index": 6,
            "gender": "女",
            "longitude": 116.4,
            "use_solar_time": True,
            "birth_hour": 12,
            "birth_minute": 0,
        }
        info = BirthInfo.from_dict(data)
        assert info.longitude == 116.4
        assert info.use_solar_time is True
        assert info.birth_hour == 12
        assert info.get_adjusted_time_index() == 6
