"""
数据缓冲平台 - SQLite 到 MySQL 数据迁移脚本测试
"""
import logging
from datetime import datetime
from unittest.mock import MagicMock, patch

import pytest
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

from app.models import Base, MarketData, SymbolTimestamp


def _create_sqlite_source(tmp_path):
    """创建包含测试数据的文件 SQLite 源数据库。"""
    db_path = tmp_path / "test_source.db"
    engine = create_engine(f"sqlite:///{db_path}")
    MarketData.__table__.create(bind=engine)
    SymbolTimestamp.__table__.create(bind=engine)
    Session = sessionmaker(bind=engine)
    session = Session()
    fetched_at = datetime(2026, 7, 4, 10, 0, 0)
    session.add_all(
        [
            MarketData(
                symbol="AG2606",
                data_type="futures",
                period="5min",
                candles_json='[{"open": 100, "close": 110}]',
                current_price=123.45,
                fetched_at=fetched_at,
                candle_count=1,
            ),
            MarketData(
                symbol="AG2606",
                data_type="futures",
                period="15min",
                candles_json='[{"open": 105, "close": 115}]',
                current_price=123.45,
                fetched_at=fetched_at,
                candle_count=1,
            ),
            SymbolTimestamp(
                symbol="AG2606",
                data_type="futures",
                last_refresh_at=fetched_at,
                refresh_count=5,
            ),
        ]
    )
    session.commit()
    session.close()
    return engine


def _make_mysql_session(market_data_count=0, symbol_timestamp_count=0):
    """构造模拟 MySQL 会话，支持记录写入和计数查询。"""
    mysql_session = MagicMock()
    mysql_session.__enter__ = MagicMock(return_value=mysql_session)
    mysql_session.__exit__ = MagicMock(return_value=False)

    added_market_data = []
    added_symbol_timestamps = []

    def mock_query(model):
        query = MagicMock()
        if model is MarketData:
            query.count.return_value = market_data_count
        elif model is SymbolTimestamp:
            query.count.return_value = symbol_timestamp_count
        return query

    mysql_session.query.side_effect = mock_query
    mysql_session.add_all.side_effect = lambda records: (
        added_market_data.extend(records)
        if records and isinstance(records[0], MarketData)
        else added_symbol_timestamps.extend(records)
    )

    return mysql_session, added_market_data, added_symbol_timestamps


class TestMigrateSqliteToMysql:
    """migrate_sqlite_to_mysql 行为测试。"""

    def test_migrates_data_when_mysql_tables_are_empty(self, caplog, tmp_path):
        """MySQL 表为空时，应从 SQLite 迁移全部数据。"""
        sqlite_engine = _create_sqlite_source(tmp_path)
        mysql_session, added_market_data, added_symbol_timestamps = _make_mysql_session(
            market_data_count=0, symbol_timestamp_count=0
        )

        with patch("app.migration.create_engine", return_value=sqlite_engine):
            with patch(
                "app.migration.MySQLSessionLocal", return_value=mysql_session
            ):
                with caplog.at_level(logging.INFO, logger="app.migration"):
                    from app.migration import migrate_sqlite_to_mysql

                    result = migrate_sqlite_to_mysql()

        assert result is True
        assert len(added_market_data) == 2
        assert len(added_symbol_timestamps) == 1
        assert added_market_data[0].symbol == "AG2606"
        assert added_market_data[0].period == "5min"
        assert added_symbol_timestamps[0].symbol == "AG2606"
        assert "迁移完成" in caplog.text
        assert "market_data: 2" in caplog.text
        assert "symbol_timestamps: 1" in caplog.text
        mysql_session.commit.assert_called_once()

    def test_skips_migration_when_mysql_has_data(self, caplog, tmp_path):
        """MySQL 表非空时，应跳过迁移并输出提示日志。"""
        sqlite_engine = _create_sqlite_source(tmp_path)
        mysql_session, added_market_data, added_symbol_timestamps = _make_mysql_session(
            market_data_count=1, symbol_timestamp_count=1
        )

        with patch("app.migration.create_engine", return_value=sqlite_engine):
            with patch(
                "app.migration.MySQLSessionLocal", return_value=mysql_session
            ):
                with caplog.at_level(logging.INFO, logger="app.migration"):
                    from app.migration import migrate_sqlite_to_mysql

                    result = migrate_sqlite_to_mysql()

        assert result is False
        assert len(added_market_data) == 0
        assert len(added_symbol_timestamps) == 0
        assert "MySQL 已存在数据" in caplog.text
        assert "跳过迁移" in caplog.text
        mysql_session.commit.assert_not_called()

    def test_migrates_empty_table_when_other_table_has_data(self, caplog, tmp_path):
        """按表分别判断：仅空表需要迁移，非空表跳过。"""
        sqlite_engine = _create_sqlite_source(tmp_path)
        mysql_session, added_market_data, added_symbol_timestamps = _make_mysql_session(
            market_data_count=1, symbol_timestamp_count=0
        )

        with patch("app.migration.create_engine", return_value=sqlite_engine):
            with patch(
                "app.migration.MySQLSessionLocal", return_value=mysql_session
            ):
                with caplog.at_level(logging.INFO, logger="app.migration"):
                    from app.migration import migrate_sqlite_to_mysql

                    result = migrate_sqlite_to_mysql()

        assert result is True
        assert len(added_market_data) == 0
        assert len(added_symbol_timestamps) == 1
        assert added_symbol_timestamps[0].symbol == "AG2606"
        mysql_session.commit.assert_called_once()
