"""A module containing pre-defined references."""

from __future__ import annotations

from collections.abc import Sequence
from typing import Literal

from typing_extensions import TypeAlias

from .api import NamedReference


def _r(p: str, i: str, n: str) -> NamedReference:
    return NamedReference(prefix=p, identifier=i, name=n)


# RDF Schema

has_label = _r("rdfs", "label", "has label")
see_also = _r("rdfs", "seeAlso", "see also")
has_comment = _r("rdfs", "comment", "comment")

is_a = subclass_of = _r("rdfs", "subClassOf", "subclass of")
subproperty_of = _r("rdfs", "subPropertyOf", "subproperty of")
rdf_type = _r("rdf", "type", "instance of")

# XML Schema

xsd_string = _r("xsd", "string", "string")
xsd_float = _r("xsd", "float", "float")
xsd_decimal = _r("xsd", "decimal", "decimal")
xsd_integer = _r("xsd", "integer", "integer")
xsd_boolean = _r("xsd", "boolean", "boolean")
xsd_year = _r("xsd", "gYear", "year")
xsd_uri = _r("xsd", "anyURI", "URI")
xsd_date = _r("xsd", "date", "date")
xsd_datetime = _r("xsd", "dateTime", "datetime")

# DC Terms

has_description = _r("dcterms", "description", "description")
has_license = _r("dcterms", "license", "license")
has_title = _r("dcterms", "title", "title")
has_contributor = _r("dcterms", "contributor", "contributor")
has_source = _r("dcterms", "source", "source")
has_date = _r("dcterms", "date", "date")

# OBO in OWL

obo_in_subset = _r("oboInOwl", "inSubset", "in subset")
obo_has_namespace = _r("oboInOwl", "hasOBONamespace", "has OBO namespace")
obo_autogenerated_by = _r("oboInOwl", "auto-generated-by", "auto-generated by")
obo_creation_date = _r("oboInOwl", "creation_date", "creation date")

# OWL

owl_version_info = _r("owl", "versionInfo", "has version")
owl_inverse_of = _r("owl", "inverseOf", "inverse of")

# IAO

has_ontology_root_term = _r("IAO", "0000700", "has ontology root term")
has_term_editor = _r("IAO", "0000117", "term editor")

# Synonyms

## Synonym Relations

#: The parent property for having synonyms, see :data:`synonym_scopes`
#: for a dictionary of all synonym types
has_synonym = _r("oboInOwl", "hasSynonym", "has synonym")

has_exact_synonym = _r("oboInOwl", "hasExactSynonym", "has exact synonym")
has_narrow_synonym = _r("oboInOwl", "hasNarrowSynonym", "has narrow synonym")
has_broad_synonym = _r("oboInOwl", "hasBroadSynonym", "has broad synonym")
has_related_synonym = _r("oboInOwl", "hasRelatedSynonym", "has related synonym")

#: A list of strings used to refer to synonym types in ``oboInOwl``
SynonymScope: TypeAlias = Literal["EXACT", "NARROW", "BROAD", "RELATED"]

#: This is what happens if no specificity is given
DEFAULT_SYNONYM_SCOPE: SynonymScope = "RELATED"

# OBO Graph JSON uses the LUIDs from OBO in OWL, so we
# keep track of these as well
SynonymScopeOIO: TypeAlias = Literal[
    "hasExactSynonym",
    "hasNarrowSynonym",
    "hasBroadSynonym",
    "hasRelatedSynonym",
]

DEFAULT_SYNONYM_SCOPE_OIO: SynonymScopeOIO = "hasRelatedSynonym"

#: A mapping from synonym scopes to references
synonym_scopes: dict[SynonymScope, NamedReference] = {
    "EXACT": has_exact_synonym,
    "NARROW": has_narrow_synonym,
    "BROAD": has_broad_synonym,
    "RELATED": has_related_synonym,
}

synonym_scope_to_oio: dict[SynonymScope, SynonymScopeOIO] = {
    "EXACT": "hasExactSynonym",
    "NARROW": "hasNarrowSynonym",
    "BROAD": "hasBroadSynonym",
    "RELATED": "hasRelatedSynonym",
}

## OMO Synonym Types

#: The parent class for all synonym types, see :data:`synonym_types`
#: for the set of all synonym types
synonym_type = _r("oboInOwl", "SynonymType", "synonym type")

abbreviation = _r("OMO", "0003000", "abbreviation")
ambiguous_synonym = _r("OMO", "0003001", "ambiguous synonym")
dubious_synonym = _r("OMO", "0003002", "dubious synonym")
layperson_synonym = _r("OMO", "0003003", "layperson synonym")
plural_form = _r("OMO", "0003004", "plural form")
uk_spelling = _r("OMO", "0003005", "UK spelling synonym")
misspelling = _r("OMO", "0003006", "misspelling")
misnomer = _r("OMO", "0003007", "misnomer")
previous_name = _r("OMO", "0003008", "previous name")
legal_name = _r("OMO", "0003009", "legal name")
inn = _r("OMO", "0003010", "International Nonproprietary Name")
latin_term = _r("OMO", "0003011", "latin term")
acronym = _r("OMO", "0003012", "acronym")
#: Provisional, see https://github.com/information-artifact-ontology/ontology-metadata/pull/162/files
brand_name = _r("OMO", "0003013", "brand name")

#: A set of synonym types from OMO
synonym_types: set[NamedReference] = {
    abbreviation,
    ambiguous_synonym,
    dubious_synonym,
    layperson_synonym,
    plural_form,
    uk_spelling,
    misspelling,
    misnomer,
    previous_name,
    legal_name,
    inn,
    latin_term,
    acronym,
    brand_name,
}

# Semantic Mappings

## Mapping Relations

#: The parent property for semantic mappings from ``skos``, see :data:`semantic_mapping_scopes`
#: for a list of all usable properties
match = _r("skos", "mappingRelation", "is in mapping relation with")

has_dbxref = _r("oboInOwl", "hasDbXref", "has database cross-reference")
same_as = _r("owl", "sameAs", "same as")
equivalent_class = _r("owl", "equivalentClass", "equivalent class")
equivalent_property = _r("owl", "equivalentProperty", "equivalent property")

exact_match = _r("skos", "exactMatch", "exact match")
narrow_match = _r("skos", "narrowMatch", "narrow match")
broad_match = _r("skos", "broadMatch", "broad match")
close_match = _r("skos", "closeMatch", "close match")
related_match = _r("skos", "relatedMatch", "related match")

# `alternate term` and `term replaced by` can also be considered as mapping relations
alternative_term = _r("IAO", "0000118", "alternative term")
term_replaced_by = _r("IAO", "0100001", "term replaced by")

#: A list of strings used to refer to mapping types in ``skos``
SemanticMappingScope: TypeAlias = Literal["EXACT", "NARROW", "BROAD", "CLOSE", "RELATED"]

#: A mapping from mapping types to references
semantic_mapping_scopes: dict[SemanticMappingScope, NamedReference] = {
    "EXACT": exact_match,
    "NARROW": narrow_match,
    "BROAD": broad_match,
    "CLOSE": close_match,
    "RELATED": related_match,
}

## Matching Process Types

#: The parent class for matching processes, see :data:`matching_processes`
#: for the set of all matching processes.
matching_process = _r("semapv", "Matching", "matching process")

background_knowledge_based_matching_process = _r(
    "semapv",
    "BackgroundKnowledgeBasedMatching",
    "background knowledge-based matching process",
)
composite_matching_process = _r("semapv", "CompositeMatching", "composite matching process")
instance_based_matching_process = _r(
    "semapv", "InstanceBasedMatching", "instance-based matching process"
)
lexical_matching_process = _r("semapv", "LexicalMatching", "lexical matching process")
lexical_similarity_threshold_based_matching_process = _r(
    "semapv",
    "LexicalSimilarityThresholdMatching",
    "lexical similarity threshold-based matching process",
)
logical_reasoning_matching_process = _r(
    "semapv", "LogicalReasoning", "logical reasoning matching process"
)
manual_mapping_curation = _r("semapv", "ManualMappingCuration", "manual mapping curation")
mapping_chaining = _r("semapv", "MappingChaining", "mapping chaining-based matching process")
mapping_inversion = _r("semapv", "MappingInversion", "mapping inversion-based matching process")
semantic_similarity = _r(
    "semapv",
    "SemanticSimilarityThresholdMatching",
    "semantic similarity threshold-based matching process",
)
structural_matching = _r("semapv", "StructuralMatching", "structural matching process")
unspecified_matching_process = _r("semapv", "UnspecifiedMatching", "unspecified matching process")

#: A set of matching types from ``semapv``
matching_processes: set[NamedReference] = {
    background_knowledge_based_matching_process,
    composite_matching_process,
    instance_based_matching_process,
    lexical_matching_process,
    lexical_similarity_threshold_based_matching_process,
    logical_reasoning_matching_process,
    manual_mapping_curation,
    mapping_chaining,
    mapping_inversion,
    semantic_similarity,
    structural_matching,
    unspecified_matching_process,
}

#: See https://mapping-commons.github.io/sssom/spec-model/
match_typedefs: Sequence[NamedReference] = (
    broad_match,
    close_match,
    exact_match,
    narrow_match,
    related_match,
    same_as,  # for instances
    equivalent_class,  # for classes
    equivalent_property,  # for properties
    has_dbxref,
    see_also,
)

# Extension past the SSSOM spec
extended_match_typedefs: Sequence[NamedReference] = (
    *match_typedefs,
    alternative_term,
    term_replaced_by,
)

# Classes

human = _r("NCBITaxon", "9606", "Homo sapiens")

# Individuals

#: the author of this package. It's useful to have this reference pre-defined
#: for testing purposes
charlie = _r("orcid", "0000-0003-4423-4370", "Charles Tapley Hoyt")

part_of = _r("BFO", "0000050", "part of")
has_part = _r("BFO", "0000051", "has part")
located_in = _r("RO", "0001025", "located in")
enables = _r("RO", "0002327", "enables")
involved_in = _r("RO", "0002331", "involved in")
colocalizes_with = _r("RO", "0002325", "colocalizes with")
acts_upstream_of = _r("RO", "0002263", "acts upstream of")
is_active_in = _r("RO", "0002432", "is active in")
contributes_to = _r("RO", "0002326", "contributes to")
acts_upstream_of_or_within = _r("RO", "0002264", "acts upstream of or within")
acts_upstream_of_or_within_positive_effect = _r(
    "RO", "0004032", "acts upstream of or within, positive effect"
)
acts_upstream_of_or_within_negative_effect = _r(
    "RO", "0004033", "acts upstream of or within, negative effect"
)
acts_upstream_of_positive_effect = _r("RO", "0004034", "acts upstream of, positive effect")
acts_upstream_of_negative_effect = _r("RO", "0004035", "acts upstream of, negative effect")

molecularly_interacts_with = _r("ro", "0002436", "molecularly interacts with")
regulator_of = _r("RO", "0012004", "is small molecule regulator of")
inhibitor_of = _r("RO", "0012006", "is small molecule inhibitor of")

is_model_of = _r("RO", "0003301", "is model of")
is_marker_for = _r("RO", "0002607", "is marker for")
