# copyright 2022-2023 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact https://www.logilab.fr -- mailto:contact@logilab.fr
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, either version 2.1 of the License, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from typing import cast, Dict

from cubicweb.entities.authobjs import CWUser
from cubicweb.server.repository import Repository
from cubicweb.server.session import Connection


class ApiTransaction:
    def __init__(self, repo: Repository, user: CWUser):
        """
        Class defining transactions.
        A transaction allows to make several request which only take effect
        when the changes are committed.

        :param repo: The CubicWeb repository
        :param user: The user initiating the transaction
        """
        self.cnx = Connection(repo, user)
        self.cnx.__enter__()
        self._uuid = cast(str, self.cnx.transaction_uuid(set=True))

    @property
    def uuid(self) -> str:
        """
        :return: The unique identifier for this transaction
        """
        return self._uuid

    def execute(self, rql: str, params: Dict[str, any]):
        """
        Executes the given rql query on this transaction.

        :param rql: The RQL query string
        :param params: The parameters for the RQL query
        :return: The result from executing the query
        """
        return self.cnx.execute(rql, params)

    def commit(self):
        """
        Commits the current transaction to apply all changes.
        """
        self.cnx.commit()

    def rollback(self):
        """
        Rollback the current transaction to cancel all changes.
        """
        self.cnx.rollback()

    def end(self):
        self.cnx.__exit__(None, None, None)


class ApiTransactionsRepository:
    def __init__(self, repo: Repository):
        """
        Class holding all active transactions from all users.

        :param repo: The CubicWeb repository
        """
        self._transactions: Dict[str, ApiTransaction] = dict()
        self._repo = repo

    def begin_transaction(self, user: CWUser) -> str:
        """
        Starts a new transaction for the given user.

        :param user: The user initiating the transaction
        :return: The transaction uuid
        """
        transaction = ApiTransaction(self._repo, user)
        self._transactions[transaction.uuid] = transaction
        return transaction.uuid

    def end_transaction(self, uuid: str):
        """
        Ends a transaction identified by its uuid.

        :param uuid: The id of the transaction to end.
        """
        transaction = self._transactions.pop(uuid)
        transaction.end()

    def __getitem__(self, uuid: str) -> ApiTransaction:
        return self._transactions[uuid]