class DataBase:
    def __init__(self, **options):
        """
        Inisialisasi database dengan parameter opsional.

        :param options:
            - storage_type: 'local' (default) atau 'mongo'
            - file_name: Nama file untuk database lokal (default: 'database')
            - binary_keys: Kunci enkripsi untuk CipherHandler (default: 14151819154911914)
            - method_encrypt: metode enkripsi untuk CipherHandler (default: bytes)
            - mongo_url: URL MongoDB (untuk storage_type='mongo')
            - git_repo_path: Path untuk operasi git (default: '.')
        """
        self.os = __import__("os")
        self.json = __import__("json")
        self.datetime = __import__("datetime")
        self.pytz = __import__("pytz")
        self.subprocess = __import__("subprocess")

        self.storage_type = options.get("storage_type", "local")
        self.file_name = options.get("file_name", "database")
        self.binary_keys = options.get("binary_keys", 14151819154911914)
        self.method_encrypt = options.get("method_encrypt", "bytes")
        self.git_repo_path = options.get("git_repo_path", ".")

        if self.storage_type == "mongo":
            self.pymongo = __import__("pymongo")
            self.mongo_url = options.get("mongo_url")
            if not self.mongo_url:
                raise ValueError("mongo_url is required for MongoDB storage")
            self.client = self.pymongo.MongoClient(self.mongo_url)
            self.data = self.client[self.file_name]
        else:
            self.data_file = f"{self.file_name}.json"
            self._initialize_files()

        self.cipher = __import__("nsdev").encrypt.CipherHandler(key=self.binary_keys, method=self.method_encrypt, delimiter="/")

    def _initialize_files(self):
        if not self.os.path.exists(self.data_file):
            self._save_data({"vars": {}, "bots": []})

    def _load_data(self):
        if self.storage_type == "mongo":
            raise RuntimeError("_load_data is not applicable for MongoDB storage")
        try:
            with open(self.data_file, "r") as f:
                return self.json.load(f)
        except self.json.JSONDecodeError:
            return {"vars": {}, "bots": []}

    def _save_data(self, data):
        if self.storage_type == "mongo":
            raise RuntimeError("_save_data is not applicable for MongoDB storage")
        with open(self.data_file, "w") as f:
            self.json.dump(data, f, indent=4)

    def _git_commit(self, username, token, message="auto commit backup database"):
        try:
            self.subprocess.run(["git", "config", "--global", "user.email", "support@norsodikin.ltd"], check=True)
            self.subprocess.run(["git", "config", "--global", "user.name", "ɴᴏʀ sᴏᴅɪᴋɪɴ"], check=True)

            self.subprocess.run(["git", "add", self.data_file], cwd=self.git_repo_path, check=True)
            self.subprocess.run(["git", "commit", "-m", message], cwd=self.git_repo_path, check=True)

            push_command = f'echo "{username}:{token}" | git push'
            self.subprocess.run(push_command, shell=True, cwd=self.git_repo_path, check=True)
            return "Backup committed and pushed successfully."
        except self.subprocess.CalledProcessError as e:
            return f"Error during git operations: {e}"

    def setVars(self, user_id, query_name, value, var_key="variabel"):
        if self.storage_type == "mongo":
            update_data = {"$set": {f"{var_key}.{query_name}": value}}
            self.data.vars.update_one({"_id": user_id}, update_data, upsert=True)
        else:
            data = self._load_data()
            user_data = data["vars"].setdefault(str(user_id), {var_key: {}})
            user_data[var_key][query_name] = value
            self._save_data(data)

    def getVars(self, user_id, query_name, var_key="variabel"):
        if self.storage_type == "mongo":
            result = self.data.vars.find_one({"_id": user_id})
            return result.get(var_key, {}).get(query_name, None) if result else None
        else:
            return self._load_data().get("vars", {}).get(str(user_id), {}).get(var_key, {}).get(query_name)

    def removeVars(self, user_id, query_name, var_key="variabel"):
        if self.storage_type == "mongo":
            update_data = {"$unset": {f"{var_key}.{query_name}": ""}}
            self.data.vars.update_one({"_id": user_id}, update_data)
        else:
            data = self._load_data()
            if str(user_id) in data["vars"]:
                data["vars"][str(user_id)][var_key].pop(query_name, None)
                self._save_data(data)

    def setListVars(self, user_id, query_name, value, var_key="variabel"):
        if self.storage_type == "mongo":
            update_data = {"$push": {f"{var_key}.{query_name}": value}}
            self.data.vars.update_one({"_id": user_id}, update_data, upsert=True)
        else:
            data = self._load_data()
            user_data = data["vars"].setdefault(str(user_id), {var_key: {}})
            user_data[var_key].setdefault(query_name, []).append(value)
            self._save_data(data)

    def getListVars(self, user_id, query_name, var_key="variabel"):
        if self.storage_type == "mongo":
            result = self.data.vars.find_one({"_id": user_id})
            return result.get(var_key, {}).get(query_name, []) if result else []
        else:
            return self._load_data().get("vars", {}).get(str(user_id), {}).get(var_key, {}).get(query_name, [])

    def removeListVars(self, user_id, query_name, value, var_key="variabel"):
        if self.storage_type == "mongo":
            update_data = {"$pull": {f"{var_key}.{query_name}": value}}
            self.data.vars.update_one({"_id": user_id}, update_data)
        else:
            data = self._load_data()
            user_data = data.get("vars", {}).get(str(user_id), {}).get(var_key, {})
            if query_name in user_data and value in user_data[query_name]:
                user_data[query_name].remove(value)
                self._save_data(data)

    def removeAllVars(self, user_id, var_key="variabel"):
        if self.storage_type == "mongo":
            update_data = {"$unset": {var_key: ""}}
            self.data.vars.update_one({"_id": user_id}, update_data)
        else:
            data = self._load_data()
            data["vars"].pop(str(user_id), None)
            self._save_data(data)

    def allVars(self, user_id, var_key="variabel"):
        if self.storage_type == "mongo":
            result = self.data.vars.find_one({"_id": user_id})
            return result.get(var_key, {}) if result else {}
        else:
            return self._load_data().get("vars", {}).get(str(user_id), {}).get(var_key, {})

    def setExp(self, user_id, exp=30):
        have_exp = self.getVars(user_id, "EXPIRED_DATE")

        if not have_exp:
            now = self.datetime.datetime.now(self.pytz.timezone("Asia/Jakarta"))
        else:
            now = self.datetime.datetime.strptime(have_exp, "%Y-%m-%d %H:%M:%S").astimezone(self.pytz.timezone("Asia/Jakarta"))

        expire_date = now + self.datetime.timedelta(days=exp)
        self.setVars(user_id, "EXPIRED_DATE", expire_date.strftime("%Y-%m-%d %H:%M:%S"))

    def getExp(self, user_id):
        expired_date = self.getVars(user_id, "EXPIRED_DATE")

        if expired_date:
            exp_datetime = self.datetime.datetime.strptime(expired_date, "%Y-%m-%d %H:%M:%S").astimezone(self.pytz.timezone("Asia/Jakarta"))
            return exp_datetime.strftime("%d-%m-%Y")
        else:
            return None

    def checkAndDeleteIfExpired(self, user_id):
        user_exp = self.getExp(user_id)
        today = self.datetime.datetime.now(self.pytz.timezone("Asia/Jakarta")).strftime("%d-%m-%Y")

        if not user_exp or user_exp == today:
            self.removeAllVars(user_id)
            self.removeBot(user_id)
            return True
        return False

    def saveBot(self, user_id, api_id, api_hash, value, is_token=False):
        field = "bot_token" if is_token else "session_string"
        if self.storage_type == "mongo":
            update_data = {
                "$set": {
                    "api_id": self.cipher.encrypt(str(api_id)),
                    "api_hash": self.cipher.encrypt(api_hash),
                    field: self.cipher.encrypt(value),
                }
            }
            self.data.bot.update_one({"user_id": user_id}, update_data, upsert=True)
        else:
            data = self._load_data()
            entry = {
                "user_id": user_id,
                "api_id": self.cipher.encrypt(str(api_id)),
                "api_hash": self.cipher.encrypt(api_hash),
                field: self.cipher.encrypt(value),
            }
            data["bots"].append(entry)
            self._save_data(data)

    def getBots(self, is_token=False):
        field = "bot_token" if is_token else "session_string"
        if self.storage_type == "mongo":
            return [
                {
                    "name": str(bot_data["user_id"]),
                    "api_id": int(self.cipher.decrypt(str(bot_data["api_id"]))),
                    "api_hash": self.cipher.decrypt(bot_data["api_hash"]),
                    field: self.cipher.decrypt(bot_data.get(field)),
                }
                for bot_data in self.data.bot.find({"user_id": {"$exists": 1}})
            ]
        else:
            return [
                {
                    "name": str(bot_data["user_id"]),
                    "api_id": int(self.cipher.decrypt(str(bot_data["api_id"]))),
                    "api_hash": self.cipher.decrypt(bot_data["api_hash"]),
                    field: self.cipher.decrypt(bot_data.get(field)),
                }
                for bot_data in self._load_data()["bots"]
            ]

    def removeBot(self, user_id):
        if self.storage_type == "mongo":
            self.data.bot.delete_one({"user_id": user_id})
        else:
            data = self._load_data()
            data["bots"] = [bot for bot in data["bots"] if bot["user_id"] != user_id]
            self._save_data(data)
