# Copyright 2021 Vincent Texier <vit@free.fr>
#
# This software is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This software 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 General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
import base64
import json
from collections import OrderedDict
from datetime import datetime

# import re
#
# # from copy import copy
from hashlib import sha256
from typing import Optional

import base58

from tikka.adapters.network.datapod.connection import DataPodConnection
from tikka.domains.entities.profile import Profile
from tikka.interfaces.adapters.network.connection import NetworkConnectionError
from tikka.interfaces.adapters.network.datapod.profiles import (
    DataPodProfilesException,
    DataPodProfilesInterface,
)
from tikka.libs.keypair import Keypair


class DataPodProfiles(DataPodProfilesInterface):
    """
    DataPodProfiles class
    """

    def get(self, address: str) -> Optional[Profile]:
        __doc__ = (  # pylint: disable=redefined-builtin, unused-variable
            DataPodProfilesInterface.get.__doc__
        )
        connection: DataPodConnection = self.datapod.connection
        if not connection.is_connected() or connection.client is None:
            raise DataPodProfilesException(NetworkConnectionError())

        # for now, we use cesium+ V1 datapods
        # the datapod validate signature from v1 address (base58 public key)
        # so we need to convert V2 address to V1 address
        keypair = Keypair(address)
        v1_address = base58.b58encode(keypair.public_key).decode()

        try:
            result_string = connection.execute_query(
                endpoint=f"/user/profile/{v1_address}/_source"
            )
        except Exception as exception:
            if "HTTP 404:" in exception.args[0]:
                return None

            raise DataPodProfilesException(exception)
        #
        # {
        #    'title': 'Jean Dupond',
        #    'description': 'boulanger',
        #    'socials': [
        #       {
        #           'type': 'web',
        #           'url': 'http://jean.boulanger.fr'
        #        }
        #     ],
        #     'avatar': {
        #       '_content_type': 'image/png',
        #       '_content': 'iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAAgAuOuLp3zkbkD/w8GeTbwGV2uCwAAAABJRU5ErkJggg=='
        #     },
        #     'time': 1584271238,
        #     'issuer': 'xxx',
        #     'hash': 'yyy',
        #     'signature': 'xYPDg==',
        #     'city': 'Paris, 75020',
        #     'geoPoint': {
        #       'lon': 1.0,
        #       'lat': 48.0
        #     },
        #     'version': 2
        #  }
        #

        #        cleaned_json = serialize_document_for_hash(result_string)
        # # check hash
        # # Regex pour supprimer "hash": "xxx" (avec différents formats d'espaces)
        # pattern1 = r"\"hash\"\s*:\s*\"[^\"]*\"\s*,?"
        # # Regex pour supprimer "signature": "xxx"
        # pattern2 = r"\"signature\"\s*:\s*\"[^\"]*\"\s*,?"
        #
        # # Supprimer les champs sensibles
        # cleaned_json = re.sub(pattern1, "", result_string, flags=re.MULTILINE)
        # cleaned_json = re.sub(pattern2, "", cleaned_json, flags=re.MULTILINE)
        #
        # # Nettoyer les virgules orphelines (qui pourraient casser le JSON)
        # cleaned_json = re.sub(r",\s*}", "}", cleaned_json)  # Virgule avant }
        # cleaned_json = re.sub(r",\s*,", ",", cleaned_json)  # Double virgule

        # check_hash = sha256(cleaned_json.encode()).hexdigest()
        # check_hash_deepseek = calculate_hash_java_compatible(result_string)

        result = json.loads(result_string, object_pairs_hook=OrderedDict)
        # if check_hash != result["hash"].lower():
        #     return None

        # check signature

        return Profile(address=address, data=result)

    def add(self, keypair: Keypair, profile: Profile) -> None:
        __doc__ = (  # pylint: disable=redefined-builtin, unused-variable
            DataPodProfilesInterface.add.__doc__
        )
        connection: DataPodConnection = self.datapod.connection
        if not connection.is_connected() or connection.client is None:
            raise DataPodProfilesException(NetworkConnectionError())

        v1_address = base58.b58encode(keypair.public_key).decode()

        document = profile.data
        # remove old hash and signature
        if "hash" in document:
            del document["hash"]
        if "signature" in document:
            del document["signature"]

        document["time"] = int(datetime.now().timestamp())
        document["issuer"] = v1_address
        hash = sha256(json.dumps(document).encode()).hexdigest().upper()
        signature = keypair.sign(data=hash)
        signature_base64 = base64.b64encode(signature).decode("UTF8")
        document["hash"] = hash
        document["signature"] = signature_base64
        # {
        #     "version" : 2,
        #     "title" : "Pecquot Ludovic",
        #     "description" : "Développeur Java et techno client-serveur\nParticipation aux #RML7, #EIS et #Sou",
        #     "time" : 1488359903,
        #     "issuer" : "2v6tXNxGC1BWaJtUFyPJ1wJ8rbz9v1ZVU1E1LEV2v4ss",
        #     "hash" : "F66D43ECD4D38785F424ADB68B3EA13DD56DABDE275BBE780E81E8D4E1D0C5FA",
        #     "signature" : "3CWxdLtyY8dky97RZBFLfP6axnfW8KUmhlkiaXC7BN98yg6xE9CkijRBGmuyrx3llPx5HeoGLG99DyvVIKZuCg=="
        # }
        try:
            connection.execute_query(
                endpoint="/user/profile", method="POST", data=document
            )
        except Exception as exception:
            raise DataPodProfilesException(exception)

    def delete(self, keypair: Keypair) -> None:
        __doc__ = (  # pylint: disable=redefined-builtin, unused-variable
            DataPodProfilesInterface.delete.__doc__
        )
        connection: DataPodConnection = self.datapod.connection
        if not connection.is_connected() or connection.client is None:
            raise DataPodProfilesException(NetworkConnectionError())

        v1_address = base58.b58encode(keypair.public_key).decode()

        document = {
            "version": 2,
            "index": "user",
            "type": "profile",
            "id": v1_address,
            "issuer": v1_address,
            "time": int(datetime.now().timestamp()),
        }
        hash = sha256(json.dumps(document).encode()).hexdigest().upper()
        signature = keypair.sign(data=hash)
        signature_base64 = base64.b64encode(signature).decode("UTF8")
        document["hash"] = hash
        document["signature"] = signature_base64
        print(document)
        try:
            connection.execute_query(
                endpoint="/history/delete",
                method="POST",
                data=document
                # endpoint="/user/profile/_delete", method="POST", data=document
            )
        except Exception as exception:
            raise DataPodProfilesException(exception)


def calculate_hash_java_compatible(json_string):
    """
    Calcule le hash de la même manière que le code Java
    en conservant l'ordre des champs
    """
    # Parser le JSON en OrderedDict pour conserver l'ordre
    data = json.loads(json_string, object_pairs_hook=OrderedDict)

    # Créer un nouveau OrderedDict sans les champs sensibles
    data_for_hash = OrderedDict()
    for key, value in data.items():
        if key not in ["hash", "signature", "read_signature"]:
            data_for_hash[key] = value

    # Resérialiser exactement comme le fait Jackson
    json_for_hash = json.dumps(
        data_for_hash,
        separators=(",", ":"),  # Pas d'espaces, comme Jackson
        ensure_ascii=False
        # L'ordre est conservé par l'OrderedDict
    )

    return sha256(json_for_hash.encode()).hexdigest()


def serialize_document_for_hash(data: str) -> str:
    """
    Serialise Cesium+ document to get a corrct hash...

    :param data: Source Json Document as string
    :return:
    """
    serialized_json = OrderedDict(
        {
            "title": "",
            "description": "",
            "address": "",
            "city": "",
            "socials": [],
            "time": 0,
            "issuer": "",
            "version": 2,
            "tags": [],
            "geoPoint": {"lon": 0, "lat": 0},
            "avatar": {"_content_type": "", "_content": ""},
        }
    )

    for key, value in json.loads(data).items():
        if key not in ("hash", "signature", "read_signature"):
            serialized_json[key] = value

    return json.dumps(serialized_json, ensure_ascii=False, separators=(",", ":"))
