# Copyright  2014-2022 Vincent Texier <vit@free.fr>
#
# DuniterPy 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.
#
# DuniterPy 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 unittest

import pytest

from duniterpy.constants import EMPTY_HASH, G1_TEST_CURRENCY_CODENAME
from duniterpy.documents import Certification, Identity, Revocation
from duniterpy.documents.block import BlockID

PUBKEY = "A"
UID = "test"
BLOCK_ID = "0-NRSATU"
IDENTITY_KWARGS = [PUBKEY, UID, BLOCK_ID]

selfcert_inlines = [
    "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:\
h/H8tDIEbfA4yxMQcvfOXVDQhi1sUa9qYtPKrM59Bulv97ouwbAvAsEkC1Uyit1IOpeAV+CQQs4IaAyjE8F1Cw==:\
32-DB30D958EE5CB75186972286ED3F4686B8A1C2CD:lolcat\n",
    "RdrHvL179Rw62UuyBrqy2M1crx7RPajaViBatS59EGS:\
Ah55O8cvdkGS4at6AGOKUjy+wrFwAq8iKRJ5xLIb6Xdi3M8WfGOUdMjwZA6GlSkdtlMgEhQPm+r2PMebxKrCBg==:\
36-1076F10A7397715D2BEE82579861999EA1F274AC:lolmouse\n",
]

cert_inlines = [
    "8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU:HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:\
0:TgmDuMxZdyutroj9jiLJA8tQp/389JIzDKuxW5+h7GIfjDu1ZbwI7HNm5rlUDhR2KreaV/QJjEaItT4Cf75rCQ==\n",
    "9fx25FmeBDJcikZLWxK5HuzKNbY6MaWYXoK1ajteE42Y:8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU:12:\
qn/XNJjaGIwfnR+wGrDME6YviCQbG+ywsQWnETlAsL6q7o3k1UhpR5ZTVY9dvejLKuC+1mUEXVTmH+8Ib55DBA==\n",
]

revokation_inline = "8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU:\
TgmDuMxZdyutroj9jiLJA8tQp/389JIzDKuxW5+h7GIfjDu1ZbwI7HNm5rlUDhR2KreaV/QJjEaItT4Cf75rCQ==\n"


class TestCertification(unittest.TestCase):
    def test_self_certification_from_inline(self):
        version = 2
        currency = "beta_brousouf"
        selfcert = Identity.from_inline(
            selfcert_inlines[0], version=version, currency=currency
        )
        self.assertEqual(
            selfcert.pubkey, "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk"
        )
        self.assertEqual(
            selfcert.signature,
            "h/H8tDIEbfA4yxMQcvfOXVDQhi1sUa9qYtPKrM59Bulv97ouwbAvAsEkC1Uyit1IOpeAV+CQQs4IaAyjE8F1Cw==",
        )
        self.assertEqual(
            str(selfcert.block_id), "32-DB30D958EE5CB75186972286ED3F4686B8A1C2CD"
        )
        self.assertEqual(selfcert.uid, "lolcat")

        selfcert = Identity.from_inline(
            selfcert_inlines[1], version=version, currency=currency
        )
        self.assertEqual(selfcert.pubkey, "RdrHvL179Rw62UuyBrqy2M1crx7RPajaViBatS59EGS")
        self.assertEqual(
            selfcert.signature,
            "Ah55O8cvdkGS4at6AGOKUjy+wrFwAq8iKRJ5xLIb6Xdi3M8WfGOUdMjwZA6GlSkdtlMgEhQPm+r2PMebxKrCBg==",
        )
        self.assertEqual(
            str(selfcert.block_id), "36-1076F10A7397715D2BEE82579861999EA1F274AC"
        )
        self.assertEqual(selfcert.uid, "lolmouse")

    def test_raw_self_certification(self):
        version = 2
        currency = "beta_brousouf"
        issuer = "HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd"
        uid = "lolcat"
        block_id = BlockID(32, "DB30D958EE5CB75186972286ED3F4686B8A1C2CD")
        signature = "J3G9oM5AKYZNLAB5Wx499w61NuUoS57JVccTShUbGpCMjCqj9yXXqNq7dyZpDWA6BxipsiaMZhujMeBfCznzyci"

        selfcert = Identity(issuer, uid, block_id, version=version, currency=currency)
        selfcert.signature = signature

        result = """Version: 2
Type: Identity
Currency: beta_brousouf
Issuer: HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd
UniqueID: lolcat
Timestamp: 32-DB30D958EE5CB75186972286ED3F4686B8A1C2CD
J3G9oM5AKYZNLAB5Wx499w61NuUoS57JVccTShUbGpCMjCqj9yXXqNq7dyZpDWA6BxipsiaMZhujMeBfCznzyci
"""
        self.assertEqual(selfcert.signed_raw(), result)

        from_raw = Identity.from_signed_raw(result)
        self.assertEqual(from_raw.signed_raw(), result)

    def test_certifications_from_inline(self):
        version = 2
        currency = "zeta_brousouf"
        cert = Certification.from_inline(None, cert_inlines[0], version, currency)
        self.assertEqual(
            cert.pubkey_from, "8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU"
        )
        self.assertEqual(cert.pubkey_to, "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk")
        self.assertEqual(cert.block_id.number, 0)
        self.assertEqual(cert.block_id.sha_hash, EMPTY_HASH)
        self.assertEqual(
            cert.signature,
            "TgmDuMxZdyutroj9jiLJA8tQp/389JIzDKuxW5+h7GIfjDu1ZbwI7HNm5rlUDhR2KreaV/QJjEaItT4Cf75rCQ==",
        )

        cert = Certification.from_inline(
            "DB30D958EE5CB75186972286ED3F4686B8A1C2CD",
            cert_inlines[1],
            version,
            currency,
        )
        self.assertEqual(
            cert.pubkey_from, "9fx25FmeBDJcikZLWxK5HuzKNbY6MaWYXoK1ajteE42Y"
        )
        self.assertEqual(cert.pubkey_to, "8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU")
        self.assertEqual(cert.block_id.number, 12)
        self.assertEqual(
            cert.block_id.sha_hash, "DB30D958EE5CB75186972286ED3F4686B8A1C2CD"
        )
        self.assertEqual(
            cert.signature,
            "qn/XNJjaGIwfnR+wGrDME6YviCQbG+ywsQWnETlAsL6q7o3k1UhpR5ZTVY9dvejLKuC+1mUEXVTmH+8Ib55DBA==",
        )

    def test_certification_raw(self):
        version = 2
        currency = "beta_brousouf"
        pubkey_from = "DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV"
        pubkey_to = "HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd"
        block_id = BlockID(36, "1076F10A7397715D2BEE82579861999EA1F274AC")
        signature = "SoKwoa8PFfCDJWZ6dNCv7XstezHcc2BbKiJgVDXv82R5zYR83nis9dShLgWJ5w48noVUHimdngzYQneNYSMV3rk"
        identity = Identity(
            pubkey_to,
            "lolcat",
            BlockID(32, "DB30D958EE5CB75186972286ED3F4686B8A1C2CD"),
            version=version,
            currency=currency,
        )
        identity.signature = "J3G9oM5AKYZNLAB5Wx499w61NuUoS57JVccTShUbGpCMjCqj9yXXqNq7dyZpDWA6BxipsiaMZhujMeBfCznzyci"

        certification = Certification(
            pubkey_from, identity, block_id, version=version, currency=currency
        )
        certification.signature = signature

        result = """Version: 2
Type: Certification
Currency: beta_brousouf
Issuer: DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV
IdtyIssuer: HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd
IdtyUniqueID: lolcat
IdtyTimestamp: 32-DB30D958EE5CB75186972286ED3F4686B8A1C2CD
IdtySignature: J3G9oM5AKYZNLAB5Wx499w61NuUoS57JVccTShUbGpCMjCqj9yXXqNq7dyZpDWA6BxipsiaMZhujMeBfCznzyci
CertTimestamp: 36-1076F10A7397715D2BEE82579861999EA1F274AC
SoKwoa8PFfCDJWZ6dNCv7XstezHcc2BbKiJgVDXv82R5zYR83nis9dShLgWJ5w48noVUHimdngzYQneNYSMV3rk
"""
        self.assertEqual(certification.signed_raw(), result)

        from_raw = Certification.from_signed_raw(certification.signed_raw())
        self.assertEqual(from_raw.signed_raw(), result)

    def test_revokation_from_inline(self):
        version = 2
        currency = "zeta_brousouf"
        revokation = Revocation.from_inline(
            revokation_inline, version=version, currency=currency
        )
        self.assertEqual(
            revokation.pubkey, "8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU"
        )
        self.assertEqual(
            revokation.signature,
            "TgmDuMxZdyutroj9jiLJA8tQp/389JIzDKuxW5+h7GIfjDu1ZbwI7HNm5rlUDhR2KreaV/QJjEaItT4Cf75rCQ==",
        )

    def test_revokation_raw(self):
        version = 2
        currency = "beta_brousouf"
        pubkey = "HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd"
        signature = "SoKwoa8PFfCDJWZ6dNCv7XstezHcc2BbKiJgVDXv82R5zYR83nis9dShLgWJ5w48noVUHimdngzYQneNYSMV3rk"
        identity = Identity(
            pubkey,
            "lolcat",
            BlockID(32, "DB30D958EE5CB75186972286ED3F4686B8A1C2CD"),
            version=version,
            currency=currency,
        )
        identity.signature = "J3G9oM5AKYZNLAB5Wx499w61NuUoS57JVccTShUbGpCMjCqj9yXXqNq7dyZpDWA6BxipsiaMZhujMeBfCznzyci"

        revokation = Revocation(identity, version=version, currency=currency)
        revokation.signature = signature

        result = """Version: 2
Type: Revocation
Currency: beta_brousouf
Issuer: HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd
IdtyUniqueID: lolcat
IdtyTimestamp: 32-DB30D958EE5CB75186972286ED3F4686B8A1C2CD
IdtySignature: J3G9oM5AKYZNLAB5Wx499w61NuUoS57JVccTShUbGpCMjCqj9yXXqNq7dyZpDWA6BxipsiaMZhujMeBfCznzyci
SoKwoa8PFfCDJWZ6dNCv7XstezHcc2BbKiJgVDXv82R5zYR83nis9dShLgWJ5w48noVUHimdngzYQneNYSMV3rk
"""
        self.assertEqual(revokation.signed_raw(), result)

    def test_revokation_from_signed_raw(self):
        signed_raw = """Version: 2
Type: Revocation
Currency: beta_brousouf
Issuer: HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd
IdtyUniqueID: lolcat
IdtyTimestamp: 32-DB30D958EE5CB75186972286ED3F4686B8A1C2CD
IdtySignature: J3G9oM5AKYZNLAB5Wx499w61NuUoS57JVccTShUbGpCMjCqj9yXXqNq7dyZpDWA6BxipsiaMZhujMeBfCznzyci
SoKwoa8PFfCDJWZ6dNCv7XstezHcc2BbKiJgVDXv82R5zYR83nis9dShLgWJ5w48noVUHimdngzYQneNYSMV3rk
"""
        revocation = Revocation.from_signed_raw(signed_raw)
        self.assertTrue(isinstance(Revocation.extract_self_cert(signed_raw), Identity))
        self.assertEqual(revocation.signed_raw(), signed_raw)


def test_certification_equality():
    cert1 = Certification(PUBKEY, Identity(*IDENTITY_KWARGS), BLOCK_ID)
    cert2 = Certification(PUBKEY, Identity(*IDENTITY_KWARGS), BLOCK_ID)
    assert cert1 == cert2


@pytest.mark.parametrize(
    "pubkey, uid, block_id, currency",
    [
        ("pubkey", UID, BLOCK_ID, None),
        (PUBKEY, "uid", BLOCK_ID, None),
        (PUBKEY, UID, "1-TEST", None),
        IDENTITY_KWARGS + [G1_TEST_CURRENCY_CODENAME],
    ],
)
def test_certification_inequality(pubkey, uid, block_id, currency):
    cert1 = Certification(pubkey, Identity(*IDENTITY_KWARGS), block_id)
    idty2 = Identity(pubkey, uid, block_id)
    cert2 = Certification(pubkey, idty2, block_id)
    if currency:
        cert2.currency = currency
    assert not cert1 == cert2
