# 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.

"""Test GridFSBucket class."""

import copy
import datetime
import os
import re
import sys
from json import loads

sys.path[0:0] = [""]

from test import IntegrationTest, unittest

import gridfs
from bson import Binary
from bson.int64 import Int64
from bson.json_util import object_hook
from gridfs.errors import CorruptGridFile, NoFile

# Commands.
_COMMANDS = {
    "delete": lambda coll, doc: [coll.delete_many(d["q"]) for d in doc["deletes"]],
    "insert": lambda coll, doc: coll.insert_many(doc["documents"]),
    "update": lambda coll, doc: [coll.update_many(u["q"], u["u"]) for u in doc["updates"]],
}

# Location of JSON test specifications.
_TEST_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), "gridfs")


def camel_to_snake(camel):
    # Regex to convert CamelCase to snake_case. Special case for _id.
    if camel == "id":
        return "file_id"
    snake = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", camel)
    return re.sub("([a-z0-9])([A-Z])", r"\1_\2", snake).lower()


class TestAllScenarios(IntegrationTest):
    fs: gridfs.GridFSBucket
    str_to_cmd: dict

    @classmethod
    def setUpClass(cls):
        super(TestAllScenarios, cls).setUpClass()
        cls.fs = gridfs.GridFSBucket(cls.db)
        cls.str_to_cmd = {
            "upload": cls.fs.upload_from_stream,
            "download": cls.fs.open_download_stream,
            "delete": cls.fs.delete,
            "download_by_name": cls.fs.open_download_stream_by_name,
        }

    def init_db(self, data, test):
        self.cleanup_colls(
            self.db.fs.files, self.db.fs.chunks, self.db.expected.files, self.db.expected.chunks
        )

        # Read in data.
        if data["files"]:
            self.db.fs.files.insert_many(data["files"])
            self.db.expected.files.insert_many(data["files"])
        if data["chunks"]:
            self.db.fs.chunks.insert_many(data["chunks"])
            self.db.expected.chunks.insert_many(data["chunks"])

        # Make initial modifications.
        if "arrange" in test:
            for cmd in test["arrange"].get("data", []):
                for key in cmd.keys():
                    if key in _COMMANDS:
                        coll = self.db.get_collection(cmd[key])
                        _COMMANDS[key](coll, cmd)

    def init_expected_db(self, test, result):
        # Modify outcome DB.
        for cmd in test["assert"].get("data", []):
            for key in cmd.keys():
                if key in _COMMANDS:
                    # Replace wildcards in inserts.
                    for doc in cmd.get("documents", []):
                        keylist = doc.keys()
                        for dockey in copy.deepcopy(list(keylist)):
                            if "result" in str(doc[dockey]):
                                doc[dockey] = result
                            if "actual" in str(doc[dockey]):  # Avoid duplicate
                                doc.pop(dockey)