import pytest
from unittest.mock import patch, MagicMock
from sql.engines.mongo import MongoEngine


# 创建MongoEngine测试实例的fixture
@pytest.fixture
def mongo_engine():
    engine = MongoEngine()
    engine.host = "localhost"
    engine.port = 27017
    engine.user = "test_user"
    engine.password = "test_password"
    engine.instance = MagicMock()
    engine.instance.db_name = "test_db"
    return engine


# 测试带load参数的命令生成
def test_build_cmd_with_load(mongo_engine):
    # Call the method with is_load=True
    cmd = mongo_engine._build_cmd(
        db_name="test_db",
        auth_db="admin",
        slave_ok="rs.slaveOk();",
        tempfile_="/tmp/test.js",
        is_load=True,
    )

    # Expected command template
    expected_cmd = (
        "mongo --quiet -u test_user -p 'test_password' localhost:27017/admin <<\\EOF\n"
        "db=db.getSiblingDB('test_db');rs.slaveOk();load('/tmp/test.js')\nEOF"
    )

    # Assertions
    assert cmd == expected_cmd


# 测试不带load参数的命令生成
def test_build_cmd_without_load(mongo_engine):
    # Call the method with is_load=False
    cmd = mongo_engine._build_cmd(
        db_name="test_db",
        auth_db="admin",
        slave_ok="rs.slaveOk();",
        sql="db.test_collection.find()",
        is_load=False,
    )

    # Expected command template
    expected_cmd = (
        "mongo --quiet -u test_user -p 'test_password' localhost:27017/admin <<\\EOF\n"
        "db=db.getSiblingDB('test_db');rs.slaveOk();db.test_collection.find()\nEOF"
    )

    # Assertions
    assert cmd == expected_cmd


# 测试无认证信息时命令生成
def test_build_cmd_without_auth(mongo_engine):
    # Set user and password to None
    mongo_engine.user = None
    mongo_engine.password = None

    # Call the method with is_load=False
    cmd = mongo_engine._build_cmd(
        db_name="test_db",
        auth_db="admin",
        slave_ok="rs.slaveOk();",
        sql="db.test_collection.find()",
        is_load=False,
    )

    # Expected command template
    expected_cmd = (
        "mongo --quiet  localhost:27017/admin <<\\EOF\n"
        "db=db.getSiblingDB('test_db');rs.slaveOk();db.test_collection.find()\nEOF"
    )

    # Assertions
    assert cmd == expected_cmd


# 测试无认证信息且带load参数的命令生成
def test_build_cmd_with_load_without_auth(mongo_engine):
    # Set user and password to None
    mongo_engine.user = None
    mongo_engine.password = None

    # Call the method with is_load=True
    cmd = mongo_engine._build_cmd(
        db_name="test_db",
        auth_db="admin",
        slave_ok="rs.slaveOk();",
        tempfile_="/tmp/test.js",
        is_load=True,
    )

    # Expected command template
    expected_cmd = (
        "mongo --quiet  localhost:27017/admin <<\\EOF\n"
        "db=db.getSiblingDB('test_db');rs.slaveOk();load('/tmp/test.js')\nEOF"
    )

    # Assertions
    assert cmd == expected_cmd


# 参数化测试多种Mongo命令的生成
@pytest.mark.parametrize(
    "params,expected_cmd",
    [
        # 基础 find 查询
        (
            dict(
                db_name="test_db",
                auth_db="admin",
                slave_ok="",
                sql="db.test_collection.find({})",
                is_load=False,
            ),
            "mongo --quiet -u test_user -p 'test_password' localhost:27017/admin <<\\EOF\n"
            "db=db.getSiblingDB('test_db');db.test_collection.find({})\nEOF",
        ),
        # find 带条件
        (
            dict(
                db_name="test_db",
                auth_db="admin",
                slave_ok="",
                sql="db.test_collection.find({'name':'archery'})",
                is_load=False,
            ),
            "mongo --quiet -u test_user -p 'test_password' localhost:27017/admin <<\\EOF\n"
            "db=db.getSiblingDB('test_db');db.test_collection.find({'name':'archery'})\nEOF",
        ),
        # aggregate 查询
        (
            dict(
                db_name="test_db",
                auth_db="admin",
                slave_ok="",
                sql="db.test_collection.aggregate([{'$match':{'age':{'$gt':18}}}])",
                is_load=False,
            ),
            "mongo --quiet -u test_user -p 'test_password' localhost:27017/admin <<\\EOF\n"
            "db=db.getSiblingDB('test_db');db.test_collection.aggregate([{'$match':{'age':{'$gt':18}}}])\nEOF",
        ),
        # count 查询
        (
            dict(
                db_name="test_db",
                auth_db="admin",
                slave_ok="",
                sql="db.test_collection.count({'status':'active'})",
                is_load=False,
            ),
            "mongo --quiet -u test_user -p 'test_password' localhost:27017/admin <<\\EOF\n"
            "db=db.getSiblingDB('test_db');db.test_collection.count({'status':'active'})\nEOF",
        ),
        # explain 查询
        (
            dict(
                db_name="test_db",
                auth_db="admin",
                slave_ok="",
                sql="db.test_collection.find({'score':{'$gte':90}}).explain()",
                is_load=False,
            ),
            "mongo --quiet -u test_user -p 'test_password' localhost:27017/admin <<\\EOF\n"
            "db=db.getSiblingDB('test_db');db.test_collection.find({'score':{'$gte':90}}).explain()\nEOF",
        ),
        # find 带 sort/limit/skip
        (
            dict(
                db_name="test_db",
                auth_db="admin",
                slave_ok="",
                sql="db.test_collection.find({}).sort({'age':-1}).limit(10).skip(5)",
                is_load=False,
            ),
            "mongo --quiet -u test_user -p 'test_password' localhost:27017/admin <<\\EOF\n"
            "db=db.getSiblingDB('test_db');db.test_collection.find({}).sort({'age':-1}).limit(10).skip(5)\nEOF",
        ),
        # findOne 查询
        (
            dict(
                db_name="test_db",
                auth_db="admin",
                slave_ok="",
                sql="db.test_collection.findOne({'_id':ObjectId('507f1f77bcf86cd799439011')})",
                is_load=False,
            ),
            "mongo --quiet -u test_user -p 'test_password' localhost:27017/admin <<\\EOF\n"
            "db=db.getSiblingDB('test_db');db.test_collection.findOne({'_id':ObjectId('507f1f77bcf86cd799439011')})\nEOF",
        ),
        # insertOne
        (
            dict(
                db_name="test_db",
                auth_db="admin",
                slave_ok="",
                sql="db.test_collection.insertOne({'name':'archery','age':20})",
                is_load=False,
            ),
            "mongo --quiet -u test_user -p 'test_password' localhost:27017/admin <<\\EOF\n"
            "db=db.getSiblingDB('test_db');db.test_collection.insertOne({'name':'archery','age':20})\nEOF",
        ),
        # updateMany
        (
            dict(
                db_name="test_db",
                auth_db="admin",
                slave_ok="",
                sql="db.test_collection.updateMany({'status':'active'},{'$set':{'score':100}})",
                is_load=False,
            ),
            "mongo --quiet -u test_user -p 'test_password' localhost:27017/admin <<\\EOF\n"
            "db=db.getSiblingDB('test_db');db.test_collection.updateMany({'status':'active'},{'$set':{'score':100}})\nEOF",
        ),
        # deleteMany
        (
            dict(
                db_name="test_db",
                auth_db="admin",
                slave_ok="",
                sql="db.test_collection.deleteMany({'expired':true})",
                is_load=False,
            ),
            "mongo --quiet -u test_user -p 'test_password' localhost:27017/admin <<\\EOF\n"
            "db=db.getSiblingDB('test_db');db.test_collection.deleteMany({'expired':true})\nEOF",
        ),
        # createIndex
        (
            dict(
                db_name="test_db",
                auth_db="admin",
                slave_ok="",
                sql="db.test_collection.createIndex({'name':1},{'background':true})",
                is_load=False,
            ),
            "mongo --quiet -u test_user -p 'test_password' localhost:27017/admin <<\\EOF\n"
            "db=db.getSiblingDB('test_db');db.test_collection.createIndex({'name':1},{'background':true})\nEOF",
        ),
        # dropIndex
        (
            dict(
                db_name="test_db",
                auth_db="admin",
                slave_ok="",
                sql="db.test_collection.dropIndex('name_1')",
                is_load=False,
            ),
            "mongo --quiet -u test_user -p 'test_password' localhost:27017/admin <<\\EOF\n"
            "db=db.getSiblingDB('test_db');db.test_collection.dropIndex('name_1')\nEOF",
        ),
        # createCollection
        (
            dict(
                db_name="test_db",
                auth_db="admin",
                slave_ok="",
                sql="db.createCollection('new_collection')",
                is_load=False,
            ),
            "mongo --quiet -u test_user -p 'test_password' localhost:27017/admin <<\\EOF\n"
            "db=db.getSiblingDB('test_db');db.createCollection('new_collection')\nEOF",
        ),
    ],
)
def test_build_cmd_various_queries(mongo_engine, params, expected_cmd):
    # 测试多种Mongo命令的生成
    cmd = mongo_engine._build_cmd(**params)
    assert cmd == expected_cmd


# 测试带slave_ok参数的命令生成
def test_build_cmd_with_slave_ok(mongo_engine):
    cmd = mongo_engine._build_cmd(
        db_name="test_db",
        auth_db="admin",
        slave_ok="rs.slaveOk();",
        sql="db.test_collection.find()",
        is_load=False,
    )
    expected_cmd = (
        "mongo --quiet -u test_user -p 'test_password' localhost:27017/admin <<\\EOF\n"
        "db=db.getSiblingDB('test_db');rs.slaveOk();db.test_collection.find()\nEOF"
    )
    assert cmd == expected_cmd


# 测试db_name为空时命令生成
def test_build_cmd_with_empty_db_name(mongo_engine):
    cmd = mongo_engine._build_cmd(
        db_name="",
        auth_db="admin",
        slave_ok="",
        sql="db.test_collection.find()",
        is_load=False,
    )
    expected_cmd = (
        "mongo --quiet -u test_user -p 'test_password' localhost:27017/admin <<\\EOF\n"
        "db=db.getSiblingDB('');db.test_collection.find()\nEOF"
    )
    assert cmd == expected_cmd


# 测试user和password为None时命令生成
def test_build_cmd_with_none_user_password(mongo_engine):
    mongo_engine.user = None
    mongo_engine.password = None
    cmd = mongo_engine._build_cmd(
        db_name="test_db",
        auth_db="admin",
        slave_ok="",
        sql="db.test_collection.find()",
        is_load=False,
    )
    expected_cmd = (
        "mongo --quiet  localhost:27017/admin <<\\EOF\n"
        "db=db.getSiblingDB('test_db');db.test_collection.find()\nEOF"
    )
    assert cmd == expected_cmd


# 测试带load和slave_ok参数的命令生成
def test_build_cmd_with_load_and_slave_ok(mongo_engine):
    cmd = mongo_engine._build_cmd(
        db_name="test_db",
        auth_db="admin",
        slave_ok="rs.slaveOk();",
        tempfile_="/tmp/test.js",
        is_load=True,
    )
    expected_cmd = (
        "mongo --quiet -u test_user -p 'test_password' localhost:27017/admin <<\\EOF\n"
        "db=db.getSiblingDB('test_db');rs.slaveOk();load('/tmp/test.js')\nEOF"
    )
    assert cmd == expected_cmd


# 测试无认证信息且带load和slave_ok参数的命令生成
def test_build_cmd_with_load_without_auth_and_slave_ok(mongo_engine):
    mongo_engine.user = None
    mongo_engine.password = None
    cmd = mongo_engine._build_cmd(
        db_name="test_db",
        auth_db="admin",
        slave_ok="rs.slaveOk();",
        tempfile_="/tmp/test.js",
        is_load=True,
    )
    expected_cmd = (
        "mongo --quiet  localhost:27017/admin <<\\EOF\n"
        "db=db.getSiblingDB('test_db');rs.slaveOk();load('/tmp/test.js')\nEOF"
    )
    assert cmd == expected_cmd