"""Datacite interface for Geonetwork ressources."""

from collections import namedtuple
from mimetypes import guess_type

import requests

from .credentials import API, MDS, PREFIX, SESSION
from ..geonetwork import validate
from ..geonetwork.validate import GeoNetworkResource,  IDS
from ..misc import file_size


# Keywords
GEOSUNA_PUBLISHER = {
    "name": "GéOsuna",
    "publisherIdentifier": "https://www.re3data.org/repository/r3d100014346",
    "publisherIdentifierScheme": "re3data",
    "schemeUri": "https://re3data.org/",
}

GEOSUNA_DOI = {
    "relationType": "IsPublishedIn",
    "relatedIdentifier": "10.17616/R31NJNKA",
    "resourceTypeGeneral": "Service",
    "relatedIdentifierType": "DOI",
}

ROR = {
    "Observatoire des Sciences de l'Univers Nantes Atlantique (Osuna)": "02w0vb190",
    "Observatoire des Sciences de l'Univers Nantes Atlantique (OSUNA)": "02w0vb190",
    "Observatoire des Sciences de l'Univers Nantes Atlantique": "02w0vb190",
    "Osuna": "02w0vb190",
    "OSUNA": "02w0vb190",
    "Université Gustave Eiffel": "03x42jk29",
    "UGE": "03x42jk29",
}


def get_affiliation(name):
    return (
        {
            "name": name,
            "affiliationIdentifier": f"https://ror.org/{ROR[name]}",
            "affiliationIdentifierScheme": "ROR",
            "schemeUri": "https://ror.org",
        }
        if name in ROR
        else {"name": name}
    )


def get_name_identifiers(name):
    return (
        {
            "nameIdentifier": f"https://ror.org/{ROR[name]}",
            "nameIdentifierScheme": "ROR",
            "schemeUri": "https://ror.org",
        }
        if name in ROR
        else {}
    )


def get_contributor(name, affiliation, role):
    if name is None:
        name_type = "Organizational"
        name = affiliation
        key = "nameIdentifiers"
        values = [get_name_identifiers(affiliation)]
    else:
        name_type = "Personal"
        key = "affiliation"
        values = [get_affiliation(affiliation)]

    return {
        "nameType": name_type,
        "name": name,
        key: values,
        "contributorType": ROLES[role],
    }


ROLES = {
    # GeoNetwork gmxCodelists vs. DataCite contributorType in 4.3 schema
    "resourceProvider": "HostingInstitution",
    # "custodian": "DataCurator",  # <- Used in creator entry
    "owner": "ProjectManager",
    "user": "ProjectMember",
    "distributor": "Distributor",
    "originator": "Supervisor",
    # "pointOfContact": "ContactPerson",  # <- Used in creator entry
    "principalInvestigator": "ProjectLeader",
    "processor": "ProjectMember",
    "publisher": "Distributor",
    # "author": "DataCollector",  # <- Used in creator entry
}

CREATORS_ROLES = ["author", "pointOfContact", "Gestionnaire", "custodian"]

License = namedtuple("License", "name,code")

LICENSES = {
    "cc0": License("Creative Commons Zero v1.0 Universal", "CC0-1.0"),
    "cc-by": License("Creative Commons Attribution 4.0 International", "CC-BY-4.0"),
    "cc-by-sa": License(
        "Creative Commons Attribution Share Alike 4.0 International", "CC-BY-SA-4.0"
    ),
    "cc-by-nc": License(
        "Creative Commons Attribution Non Commercial 4.0 International", "CC-BY-NC-4.0"
    ),
    "cc-by-nd": License(
        "Creative Commons Attribution No Derivatives 4.0 International", "CC-BY-ND-4.0"
    ),
    "cc-by-nc-sa": License(
        "Creative Commons Attribution Non Commercial Share Alike 4.0 International",
        "CC-BY-NC-SA-4.0",
    ),
    "cc-by-nc-nd": License(
        "Creative Commons Attribution Non Commercial No Derivatives 4.0 International",
        "CC-BY-NC-ND-4.0",
    ),
    "licence-ouverte-2.0": License("Etalab Open License 2.0", "etalab-2.0"),
}


def license(key):
    lic = LICENSES[key]
    return {
        "rights": lic.name,
        "rightsUri": f"https://spdx.org/licenses/{lic.code}.html",
        "schemeUri": "https://spdx.org/licenses/",
        "rightsIdentifier": lic.code,
        "rightsIdentifierScheme": "SPDX",
    }


class GeoNetworkDOI:
    """GeoNetwork generic DOI.

    DataCite API documentation:

        https://support.datacite.org/reference/get_dois

    """

    def __init__(self, uuid):
        self.uuid = uuid

    def __str__(self):
        return self.doi

    def __repr__(self):
        return f"{self.__class__.__name__}('{self.uuid}')"

    @property
    def doi(self):
        return f"{PREFIX}/{self.uuid}"

    @property
    def payload(self):
        r = GeoNetworkResource(self.uuid)
        return {
            "data": {
                "type": "dois",
                "attributes": {
                    "schemaVersion": "http://datacite.org/schema/kernel-4",
                    "id": f"https://doi.org/{self}",
                    "doi": str(self),
                    "url": r.url,
                    "contentUrl": [url for url in r.urls],
                    "types": {
                        "schemaOrg": "Dataset",
                        "resourceTypeGeneral": "Dataset",
                        "ris": "DATA",
                    },
                    "publicationYear": r.publication_year,
                    "language": r.lang,
                    "titles": [
                        {
                            "lang": r.lang,
                            "title": r.title,
                        }
                    ],
                    "descriptions": [
                        {
                            "lang": r.lang,
                            "description": r.description,
                            "descriptionType": "Abstract",
                        }
                    ],
                    "subjects": [
                        {"subject": subject.name}
                        | ({"subjectScheme": t} if (t := subject.thesaurus) else {})
                        for subject in r.subjects
                    ],
                    "creators": [
                        {
                            "nameType": "Personal",
                            "name": creator.name,
                            "affiliation": [get_affiliation(creator.affiliation)],
                            "contributorType": "DataCollector",
                        }
                        for creator in r.contributors
                        if creator.role in CREATORS_ROLES
                    ],
                    "contributors": [
                        get_contributor(
                            contributor.name,
                            contributor.affiliation,
                            contributor.role,
                        )
                        for contributor in r.contributors + r.providers
                        if contributor.role not in CREATORS_ROLES
                    ],
                    "publisher": GEOSUNA_PUBLISHER,
                    "dates": [
                        {"date": f"{start}/{end}", "dateType": "Collected"}
                        for start, end in r.temporal_coverages
                    ]
                    + [{"date": r.publication_date, "dateType": "Issued"}],
                    "geoLocations": [
                        {"geoLocationBox": bbox._asdict()}
                        for bbox in r.spatial_coverages
                    ],
                    "relatedIdentifiers": [
                        GEOSUNA_DOI,
                    ],
                    "relatedItems": [
                        {
                            "relationType": "Documents",
                            "relatedItemType": "Dataset",
                            "relatedItemIdentifier": {
                                "relatedItemIdentifier": url,
                                "relatedItemIdentifierType": "URL",
                            },
                            "titles": [
                                {
                                    "title": url.split("/")[-1],
                                }
                            ],
                        }
                        for url in r.urls
                    ],
                    "formats": [guess_type(file.name)[0] for file in r.files],
                    "sizes": [file_size(url) for url in r.urls],
                    "rightsList": [license(r.rights)],
                },
            }
        }

    @property
    def datacite(self) -> str:
        return API.replace("api", "doi") + "/dois/" + str(self).replace("/", "%2F")

    @property
    def metadata(self) -> dict:
        resp = SESSION.get(f"{API}/dois/{self}")
        if resp.ok:
            return resp.json()

    @property
    def status(self) -> str:
        return (
            meta["data"]["attributes"]["state"]
            if (meta := self.metadata)
            else "undefined"
        )

    @property
    def media(self) -> dict:
        resp = SESSION.get(f"{MDS}/media/{self}")
        return dict(tuple(media.split("=")[::-1]) for media in resp.text.splitlines())

    def draft(self, verbose=False):
        if validate(self.uuid, verbose=verbose):
            resp = SESSION.post(f"{API}/dois", json=self.payload)
            return resp.text

    def delete(self):
        resp = SESSION.delete(f"{API}/dois/{self}")
        return resp.text

    def update(self, event, verbose=False, force=False):
        if force or validate(self.uuid, verbose=verbose):
            payload = self.payload
            payload["data"]["attributes"]["event"] = event
            resp = SESSION.put(f"{API}/dois/{self}", json=payload)
            return resp.text

    def publish(self, verbose=False, force=False):
        return self.update(event="publish", verbose=verbose, force=force)

    def register(self, verbose=False):
        return self.update(event="register", verbose=verbose)

    def hide(self):
        return self.update(event="hide", force=True)

    def badge(self, color="blue"):
        quoted_doi = self.doi.replace("/", "%2F").replace("-", "--")
        return f"- [![{self.uuid}](https://img.shields.io/badge/doi%3A-{quoted_doi}-{color})](https://doi.org/{self.doi})"
