# 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/>.
from datetime import timedelta
from typing import List

from dateutil import parser
from gql import gql

from tikka.domains.entities.identity import Certification, IdentityStatus
from tikka.interfaces.adapters.network.connection import NetworkConnectionError
from tikka.interfaces.adapters.network.indexer.certifications import (
    IndexerCertificationsException,
    IndexerCertificationsInterface,
)


class IndexerCertifications(IndexerCertificationsInterface):
    """
    IndexerCertifications class
    """

    identity_status_map = {
        "Unconfirmed": IdentityStatus.UNCONFIRMED,
        "Unvalidated": IdentityStatus.UNVALIDATED,
        "Validated": IdentityStatus.MEMBER,
        "Member": IdentityStatus.MEMBER,
        "NotMember": IdentityStatus.NOT_MEMBER,
        "Revoked": IdentityStatus.REVOKED,
        "Removed": IdentityStatus.REMOVED,
    }

    def list(
        self,
        identity_index: int,
        limit: int,
        offset: int = 0,
        sort_column: str = IndexerCertificationsInterface.COLUMN_EXPIRE_ON,
        sort_order: str = IndexerCertificationsInterface.SORT_ORDER_DESCENDING,
    ) -> List[Certification]:
        __doc__ = (  # pylint: disable=redefined-builtin, unused-variable
            IndexerCertificationsInterface.list.__doc__
        )

        if (
            not self.indexer.connection.is_connected()
            or self.indexer.connection.client is None
        ):
            raise IndexerCertificationsException(NetworkConnectionError())

        # Construire la requête GraphQL
        query = f"""
                query CertDetails {{
                blocks (
                    filter: {{ and: [
                                        {{ height: {{ greaterThanOrEqualTo: 0 }} }},
                                        {{ height: {{ lessThan: 2 }} }}
                                    ]
                            }}
                    orderBy: [HEIGHT_ASC], first: 2) {{
                    nodes {{
                      height
                      timestamp
                    }}
                  }}
                  certs(
                    first: {limit}
                    offset: {offset}
                    orderBy: {sort_column.upper()}_{sort_order.upper()}
                    filter: {{
                        or: [
                            {{ issuer: {{ index: {{ equalTo: {identity_index} }} }} }},
                            {{ receiver: {{ index: {{ equalTo: {identity_index} }} }} }}
                        ]
                    }}
                  ) {{
                    nodes {{
                          id
                          isActive
                          createdIn {{
                            block {{
                              timestamp
                            }}
                          }}
                          updatedIn {{
                            block {{
                              timestamp
                            }}
                          }}
                          expireOn
                          issuer {{
                            id
                            index
                            name
                            accountId
                            status
                          }}
                          receiver {{
                            id
                            index
                            name
                            accountId
                            status
                          }}
                    }}
                  }}
                }}
            """

        try:
            result = self.indexer.connection.client.execute(gql(query))
        except Exception as exception:
            raise IndexerCertificationsException(exception)

        # datetime are set in block since/before the block zero datetime of the blockchain
        first_two_blocks = result["blocks"]["nodes"]
        block_number_0 = first_two_blocks[0]["height"]
        assert block_number_0 == 0

        block_time_0 = parser.parse(first_two_blocks[0]["timestamp"])
        block_time_1 = parser.parse(first_two_blocks[1]["timestamp"])
        block_duration = block_time_1 - block_time_0
        avg_time_per_block = block_duration.total_seconds()

        certifications = []
        for certification in result["certs"]["nodes"]:

            expire_on_block = certification["expireOn"]

            if expire_on_block > 0:
                expire_on_time = block_time_0 + timedelta(
                    seconds=avg_time_per_block * abs(expire_on_block)
                )
            elif expire_on_block < 0:
                expire_on_time = block_time_0 - timedelta(
                    seconds=avg_time_per_block * abs(expire_on_block)
                )
            elif expire_on_block == 0:
                expire_on_time = block_time_0

            certifications.append(
                Certification(
                    id=certification["id"],
                    issuer_identity_index=certification["issuer"]["index"],
                    issuer_identity_name=certification["issuer"]["name"]
                    if certification["issuer"]["name"] != certification["issuer"]["id"]
                    else "",
                    issuer_identity_status=self.identity_status_map[
                        certification["issuer"]["status"]
                    ],
                    issuer_address=certification["issuer"]["accountId"],
                    receiver_identity_index=certification["receiver"]["index"],
                    receiver_identity_name=certification["receiver"]["name"]
                    if certification["receiver"]["name"]
                    != certification["receiver"]["id"]
                    else "",
                    receiver_identity_status=self.identity_status_map[
                        certification["receiver"]["status"]
                    ],
                    receiver_address=certification["receiver"]["accountId"],
                    created_on=parser.parse(
                        certification["createdIn"]["block"]["timestamp"]
                    ),
                    updated_on=parser.parse(
                        certification["updatedIn"]["block"]["timestamp"]
                    ),
                    expire_on=expire_on_time,
                )
            )

        return certifications
