# Gimie
# Copyright 2022 - Swiss Data Science Center (SDSC)
# A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and
# Eidgenössische Technische Hochschule Zürich (ETHZ).
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Data models to represent nodes in the graph generated by gimie."""
from __future__ import annotations
from dataclasses import dataclass, field
from datetime import datetime
import datetime
from typing import List, Optional, Union

from calamus.schema import JsonLDSchema
from calamus import fields
from rdflib import Graph

from gimie.graph.namespaces import SDO


@dataclass(order=True)
class Release:
    """
    This class represents a release of a repository.

    Parameters
    ----------
    tag: str
        The tag of the release.
    date: datetime.datetime
        The date of the release.
    commit_hash: str
        The commit hash of the release.
    """

    tag: str = field(compare=False)
    date: datetime = field(compare=True)
    commit_hash: str = field(compare=False)


@dataclass
class Organization:
    """See http//schema.org/Organization"""

    _id: str
    name: str
    legal_name: Optional[str] = None
    email: Optional[List[str]] = None
    description: Optional[str] = None
    logo: Optional[str] = None


class OrganizationSchema(JsonLDSchema):
    _id = fields.Id()
    name = fields.String(SDO.name)
    legal_name = fields.String(SDO.legalName)
    email = fields.String(SDO.email)
    description = fields.String(SDO.description)
    logo = fields.IRI(SDO.logo)

    class Meta:
        rdf_type = SDO.Organization
        model = Organization


@dataclass
class Person:
    """See http//schema.org/Person"""

    _id: str
    identifier: str
    name: Optional[str] = None
    email: Optional[str] = None
    affiliations: Optional[List[Organization]] = None

    def __str__(self):
        name = f"({self.name}) " if self.name else ""
        email = f"<{self.email}> " if self.email else ""
        orgs = (
            f"[{', '.join([org.name for org in self.affiliations])}]"
            if self.affiliations
            else ""
        )
        return f"{self.identifier} {name}{email}{orgs}".strip(" ")


class PersonSchema(JsonLDSchema):
    _id = fields.Id()
    identifier = fields.String(SDO.identifier)
    name = fields.String(SDO.name)
    affiliations = fields.Nested(
        SDO.affiliation, OrganizationSchema, many=True
    )

    class Meta:
        rdf_type = SDO.Person
        model = Person


@dataclass
class Repository:
    """This class represents a git repository.
    It does not contain any information about the content of the repository.
    See https://schema.org/SoftwareSourceCode
    """

    url: str
    name: str

    authors: Optional[List[Union[Organization, Person]]] = None
    contributors: Optional[List[Person]] = None
    date_created: Optional[datetime] = None
    date_modified: Optional[datetime] = None
    date_published: Optional[datetime] = None
    description: Optional[str] = None
    download_url: Optional[str] = None
    identifier: Optional[str] = None
    keywords: Optional[List[str]] = None
    licenses: Optional[List[str]] = None
    parent_repository: Optional[str] = None
    prog_langs: Optional[List[str]] = None
    version: Optional[str] = None

    @property
    def _id(self) -> str:
        """Unique identifier for the repository."""
        return self.url

    def to_graph(self) -> Graph:
        """Convert repository to RDF graph."""
        jd = RepositorySchema().dumps(self)
        g: Graph = Graph().parse(format="json-ld", data=str(jd))
        g.bind("schema", SDO)
        return g

    def serialize(self, format: str = "ttl", **kwargs) -> str:
        """Serialize the RDF graph representing the instance."""
        return self.to_graph().serialize(format=format, **kwargs)  # type: ignore

    def jsonld(self) -> str:
        """Alias for jsonld serialization."""
        return self.serialize(format="json-ld")


class RepositorySchema(JsonLDSchema):
    """This defines the schema used for json-ld serialization."""

    _id = fields.Id()
    authors = fields.Nested(
        SDO.author, [PersonSchema, OrganizationSchema], many=True
    )
    contributors = fields.Nested(SDO.contributor, PersonSchema, many=True)
    date_created = fields.Date(SDO.dateCreated)
    date_modified = fields.Date(SDO.dateModified)
    date_published = fields.Date(SDO.datePublished)
    description = fields.String(SDO.description)
    download_url = fields.IRI(SDO.downloadUrl)
    identifier = fields.String(SDO.identifier)
    keywords = fields.List(SDO.keywords, fields.String)
    licenses = fields.List(SDO.license, fields.IRI)
    name = fields.String(SDO.name)
    parent_repository = fields.IRI(SDO.isBasedOn)
    prog_langs = fields.List(SDO.programmingLanguage, fields.String)
    url = fields.IRI(SDO.codeRepository)
    version = fields.String(SDO.version)

    class Meta:
        rdf_type = SDO.SoftwareSourceCode
        model = Repository
        add_value_types = False
