# Copyright (C) 2010-2024 RhodeCode GmbH
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License, version 3
# (only), as published by the Free Software Foundation.
#
# 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 General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# This program is dual-licensed. If you wish to learn more about the
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/

import logging
import redis

from ..lib import rc_cache
from ..lib.ext_json import json


log = logging.getLogger(__name__)

redis_client = None


class RedisTxnClient:
    def __init__(self, url):
        self.url = url
        self._create_client(url)

    def _create_client(self, url):
        connection_pool = redis.ConnectionPool.from_url(url)
        self.writer_client = redis.StrictRedis(connection_pool=connection_pool)
        self.reader_client = self.writer_client

    def set(self, key, value, expire=24 * 60000):
        self.writer_client.set(key, value, ex=expire)

    def get(self, key):
        return self.reader_client.get(key)

    def delete(self, key):
        self.writer_client.delete(key)


def get_redis_client(url=""):
    global redis_client
    if redis_client is not None:
        return redis_client
    if not url:
        from rhodecode import CONFIG

        url = CONFIG["vcs.svn.redis_conn"]
    redis_client = RedisTxnClient(url)
    return redis_client


def extract_svn_txn_id(data: bytes):
    """
    Helper method for extraction of svn txn_id from submitted XML data during
    POST operations
    """
    import re
    from lxml import etree

    try:
        root = etree.fromstring(data)
        pat = re.compile(r"/txn/(?P<txn_id>.*)")
        for el in root:
            if el.tag == "{DAV:}source":
                for sub_el in el:
                    if sub_el.tag == "{DAV:}href":
                        match = pat.search(sub_el.text)
                        if match:
                            svn_tx_id = match.groupdict()["txn_id"]
                            return svn_tx_id
    except Exception:
        log.exception("Failed to extract txn_id")


def get_txn_id_data_key(repo_path, svn_txn_id):
    log.debug("svn-txn-id: %s, obtaining data path", svn_txn_id)
    repo_key = rc_cache.utils.compute_key_from_params(repo_path)
    final_key = f"{repo_key}.{svn_txn_id}.svn_txn_id"
    log.debug("computed final key: %s", final_key)

    return final_key


def store_txn_id_data(repo_path, svn_txn_id, data_dict):
    log.debug("svn-txn-id: %s, storing data", svn_txn_id)

    if not svn_txn_id:
        log.warning("Cannot store txn_id because it is empty")
        return

    redis_conn = get_redis_client()

    store_key = get_txn_id_data_key(repo_path, svn_txn_id)
    store_data = json.dumps(data_dict)
    redis_conn.set(store_key, store_data)


def get_txn_id_from_store(repo_path, svn_txn_id, rm_on_read=False):
    """
    Reads txn_id from store and if present returns the data for callback manager
    """
    log.debug("svn-txn-id: %s, retrieving data", svn_txn_id)
    redis_conn = get_redis_client()

    store_key = get_txn_id_data_key(repo_path, svn_txn_id)
    data = {}
    redis_conn.get(store_key)
    raw_data = "not-set"

    try:
        raw_data = redis_conn.get(store_key)
        if raw_data:
            data = json.loads(raw_data)
    except Exception:
        log.exception("Failed to get txn_id metadata: redis_conn=%s raw_data=%s", redis_conn, raw_data)

    if not data:
        log.exception("Failed to get txn_id metadata: redis_conn=%s raw_data=%s", redis_conn, raw_data)
        raise ValueError(f"Failed to get txn_id metadata, from store: {store_key}")

    if rm_on_read:
        log.debug("Cleaning up txn_id at %s", store_key)
        redis_conn.delete(store_key)

    return data
