# Copyright 2015 MongoDB, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"),;
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import annotations

from collections import namedtuple

from mockupdb import OpMsgReply, OpReply

from pymongo import ReadPreference

__all__ = ["operations", "upgrades"]


Operation = namedtuple("Operation", ["name", "function", "reply", "op_type", "not_master"])
"""Client operations on MongoDB.

Each has a human-readable name, a function that actually executes a test, and
a type that maps to one of the types in the Server Selection Spec:
'may-use-secondary', 'must-use-primary', etc.

The special type 'always-use-secondary' applies to an operation with an explicit
read mode, like the operation "command('c', read_preference=SECONDARY)".

The not-master response is how a secondary responds to a must-use-primary op,
or how a recovering member responds to a may-use-secondary op.

Example uses:

We can use "find_one" to validate that the SlaveOk bit is set when querying a
standalone, even with mode PRIMARY, but that it isn't set when sent to a mongos
with mode PRIMARY. Or it can validate that "$readPreference" is included in
mongos queries except with mode PRIMARY or SECONDARY_PREFERRED (PYTHON-865).

We can use "options_old" and "options_new" to test that the driver queries an
old server's system.namespaces collection, but uses the listCollections command
on a new server (PYTHON-857).

"secondary command" is good to test that the client can direct reads to
secondaries in a replica set, or select a mongos for secondary reads in a
sharded cluster (PYTHON-868).
"""

not_master_reply = OpMsgReply(ok=0, errmsg="not master")

operations = [
    Operation(
        "find_one",
        lambda client: client.db.collection.find_one(),
        reply={"cursor": {"id": 0, "firstBatch": []}},
        op_type="may-use-secondary",
        not_master=not_master_reply,
    ),
    Operation(
        "count_documents",
        lambda client: client.db.collection.count_documents({}),
        reply={"n": 1},
        op_type="may-use-secondary",
        not_master=not_master_reply,
    ),
    Operation(
        "estimated_document_count",
        lambda client: client.db.collection.estimated_document_count(),
        reply={"n": 1},
        op_type="may-use-secondary",
        not_master=not_master_reply,
    ),
    Operation(
        "aggregate",
        lambda client: client.db.collection.aggregate([]),
        reply={"cursor": {"id": 0, "firstBatch": []}},
        op_type="may-use-secondary",
        not_master=not_master_reply,
    ),
    Operation(
        "options",
        lambda client: client.db.collection.options(),
        reply={"cursor": {"id": 0, "firstBatch": []}},
        op_type="must-use-primary",
        not_master=not_master_reply,
    ),
    Operation(
        "command",
        lambda client: client.db.command("foo"),
        reply={"ok": 1},
        op_type="must-use-primary",  # Ignores client's read preference.
        not_master=not_master_reply,
    ),
    Operation(
        "secondary command",
        lambda client: client.db.command("foo", read_preference=ReadPreference.SECONDARY),
        reply={"ok": 1},
        op_type="always-use-secondary",
        not_master=OpReply(ok=0, errmsg="node is recovering"),
    ),
    Operation(
        "listIndexes",
        lambda client: client.db.collection.index_information(),
        reply={"cursor": {"id": 0, "firstBatch": []}},
        op_type="must-use-primary",
        not_master=not_master_reply,
    ),
]


_ops_by_name = {op.name: op for op in operations}

Upgrade = namedtuple("Upgrade", ["name", "function", "old", "new", "wire_version"])

upgrades = []
