"""Tests for api_moneyflow module.

Tests the moneyflow (个股资金流向) API wrapper.
"""

import pytest
import pandas as pd
from unittest.mock import patch, MagicMock

from src.data.api_wrappers.api_moneyflow import (
    get_moneyflow,
    sync_moneyflow,
    preview_moneyflow_sync,
    MoneyflowSync,
)


class TestMoneyflowAPI:
    """Test suite for moneyflow API wrapper."""

    @patch("src.data.api_wrappers.api_moneyflow.TushareClient")
    def test_get_moneyflow_by_date(self, mock_client_class):
        """Test fetching moneyflow data by date."""
        # Setup mock
        mock_client = MagicMock()
        mock_client_class.return_value = mock_client
        mock_client.query.return_value = pd.DataFrame(
            {
                "ts_code": ["000001.SZ", "000002.SZ"],
                "trade_date": ["20240115", "20240115"],
                "buy_sm_vol": [10000, 20000],
                "buy_sm_amount": [100.5, 200.5],
                "sell_sm_vol": [8000, 15000],
                "sell_sm_amount": [80.5, 150.5],
                "buy_md_vol": [5000, 10000],
                "buy_md_amount": [500.5, 1000.5],
                "sell_md_vol": [4000, 8000],
                "sell_md_amount": [400.5, 800.5],
                "buy_lg_vol": [2000, 5000],
                "buy_lg_amount": [2000.5, 5000.5],
                "sell_lg_vol": [1500, 4000],
                "sell_lg_amount": [1500.5, 4000.5],
                "buy_elg_vol": [1000, 3000],
                "buy_elg_amount": [5000.5, 15000.5],
                "sell_elg_vol": [800, 2500],
                "sell_elg_amount": [4000.5, 12500.5],
                "net_mf_vol": [2700, 8000],
                "net_mf_amount": [220.0, 550.0],
            }
        )

        # Test
        result = get_moneyflow(trade_date="20240115")

        # Assert
        assert not result.empty
        assert len(result) == 2
        assert "ts_code" in result.columns
        assert "trade_date" in result.columns
        assert "buy_sm_vol" in result.columns
        assert "net_mf_amount" in result.columns
        mock_client.query.assert_called_once_with("moneyflow", trade_date="20240115")

    @patch("src.data.api_wrappers.api_moneyflow.TushareClient")
    def test_get_moneyflow_by_stock(self, mock_client_class):
        """Test fetching moneyflow data by stock code."""
        # Setup mock
        mock_client = MagicMock()
        mock_client_class.return_value = mock_client
        mock_client.query.return_value = pd.DataFrame(
            {
                "ts_code": ["000001.SZ", "000001.SZ"],
                "trade_date": ["20240114", "20240115"],
                "buy_sm_vol": [10000, 11000],
                "buy_sm_amount": [100.5, 110.5],
                "sell_sm_vol": [8000, 8500],
                "sell_sm_amount": [80.5, 85.5],
                "buy_md_vol": [5000, 5500],
                "buy_md_amount": [500.5, 550.5],
                "sell_md_vol": [4000, 4200],
                "sell_md_amount": [400.5, 420.5],
                "buy_lg_vol": [2000, 2200],
                "buy_lg_amount": [2000.5, 2200.5],
                "sell_lg_vol": [1500, 1600],
                "sell_lg_amount": [1500.5, 1600.5],
                "buy_elg_vol": [1000, 1100],
                "buy_elg_amount": [5000.5, 5500.5],
                "sell_elg_vol": [800, 850],
                "sell_elg_amount": [4000.5, 4250.5],
                "net_mf_vol": [2700, 2950],
                "net_mf_amount": [220.0, 245.0],
            }
        )

        # Test
        result = get_moneyflow(
            ts_code="000001.SZ", start_date="20240101", end_date="20240131"
        )

        # Assert
        assert not result.empty
        assert len(result) == 2
        mock_client.query.assert_called_once_with(
            "moneyflow", ts_code="000001.SZ", start_date="20240101", end_date="20240131"
        )

    @patch("src.data.api_wrappers.api_moneyflow.TushareClient")
    def test_get_moneyflow_empty_response(self, mock_client_class):
        """Test handling empty response."""
        # Setup mock
        mock_client = MagicMock()
        mock_client_class.return_value = mock_client
        mock_client.query.return_value = pd.DataFrame()

        # Test
        result = get_moneyflow(trade_date="20240101")

        # Assert
        assert result.empty

    @patch("src.data.api_wrappers.api_moneyflow.TushareClient")
    def test_get_moneyflow_with_shared_client(self, mock_client_class):
        """Test fetching with shared client for rate limiting."""
        # Setup mock shared client
        mock_shared_client = MagicMock()
        mock_shared_client.query.return_value = pd.DataFrame(
            {
                "ts_code": ["000001.SZ"],
                "trade_date": ["20240115"],
                "buy_sm_vol": [10000],
                "net_mf_amount": [220.0],
            }
        )

        # Test with shared client
        result = get_moneyflow(trade_date="20240115", client=mock_shared_client)

        # Assert
        assert not result.empty
        mock_shared_client.query.assert_called_once_with(
            "moneyflow", trade_date="20240115"
        )
        mock_client_class.assert_not_called()  # Should not create new client

    @patch("src.data.api_wrappers.api_moneyflow.TushareClient")
    def test_get_moneyflow_date_column_rename(self, mock_client_class):
        """Test that 'date' column is renamed to 'trade_date'."""
        # Setup mock with 'date' column (should be renamed)
        mock_client = MagicMock()
        mock_client_class.return_value = mock_client
        mock_client.query.return_value = pd.DataFrame(
            {
                "ts_code": ["000001.SZ"],
                "date": ["20240115"],  # Using 'date' instead of 'trade_date'
                "buy_sm_vol": [10000],
                "net_mf_amount": [220.0],
            }
        )

        # Test
        result = get_moneyflow(trade_date="20240115")

        # Assert - date column should be renamed to trade_date
        assert "trade_date" in result.columns
        assert "date" not in result.columns
        assert result["trade_date"].iloc[0] == "20240115"


class TestMoneyflowSync:
    """Test suite for MoneyflowSync class."""

    def test_moneyflow_sync_class_attributes(self):
        """Test MoneyflowSync class has correct attributes."""
        assert MoneyflowSync.table_name == "moneyflow"
        assert MoneyflowSync.default_start_date == "20100101"
        assert "ts_code" in MoneyflowSync.TABLE_SCHEMA
        assert "trade_date" in MoneyflowSync.TABLE_SCHEMA
        assert "buy_sm_vol" in MoneyflowSync.TABLE_SCHEMA
        assert "net_mf_amount" in MoneyflowSync.TABLE_SCHEMA
        assert MoneyflowSync.PRIMARY_KEY == ("ts_code", "trade_date")

    @patch("src.data.api_wrappers.api_moneyflow.get_moneyflow")
    @patch("src.data.api_wrappers.api_moneyflow.TushareClient")
    def test_fetch_single_date(self, mock_client_class, mock_get_moneyflow):
        """Test fetch_single_date method."""
        # Setup mock
        mock_get_moneyflow.return_value = pd.DataFrame(
            {
                "ts_code": ["000001.SZ"],
                "trade_date": ["20240115"],
                "buy_sm_vol": [10000],
                "net_mf_amount": [220.0],
            }
        )

        # Create sync instance
        sync = MoneyflowSync()

        # Test
        result = sync.fetch_single_date("20240115")

        # Assert
        assert not result.empty
        mock_get_moneyflow.assert_called_once_with(
            trade_date="20240115", client=sync.client
        )

    @patch("src.data.api_wrappers.api_moneyflow.MoneyflowSync.sync_all")
    def test_sync_moneyflow_function(self, mock_sync_all):
        """Test sync_moneyflow convenience function."""
        # Setup mock
        mock_sync_all.return_value = pd.DataFrame(
            {
                "ts_code": ["000001.SZ"],
                "trade_date": ["20240115"],
            }
        )

        # Test
        result = sync_moneyflow(start_date="20240101", end_date="20240131")

        # Assert
        assert not result.empty
        mock_sync_all.assert_called_once_with(
            start_date="20240101", end_date="20240131", force_full=False
        )

    @patch("src.data.api_wrappers.api_moneyflow.MoneyflowSync.preview_sync")
    def test_preview_moneyflow_sync_function(self, mock_preview_sync):
        """Test preview_moneyflow_sync convenience function."""
        # Setup mock
        mock_preview_sync.return_value = {
            "sync_needed": True,
            "date_count": 31,
            "start_date": "20240101",
            "end_date": "20240131",
            "estimated_records": 10000,
            "sample_data": pd.DataFrame(),
            "mode": "incremental",
        }

        # Test
        result = preview_moneyflow_sync(start_date="20240101", end_date="20240131")

        # Assert
        assert result["sync_needed"] is True
        assert result["date_count"] == 31
        mock_preview_sync.assert_called_once_with(
            start_date="20240101", end_date="20240131", force_full=False, sample_size=3
        )
