# Copyright 2025 Terradue
#
# 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.

from .ogcapi_records_models import (
    Concept,
    Contact1,
    Email,
    Format1,
    Language,
    Link,
    RecordCommonProperties,
    RecordGeoJSON,
    Theme,
    Type7,
)
from .sciencekeywords import KEYWORDS_INDEX, ScienceKeywordRecord
from ..metadata.software_application_models import (
    AuthorRole,
    CreativeWork,
    DefinedTerm,
    Person,
    SoftwareApplication,
)
from ..metadata import Transpiler
from datetime import date, datetime, timezone
from loguru import logger

from pydantic import AnyUrl
from typing import Any, Mapping, List

import time
import uuid

SCIENCE_KEYWORDS_TERM_SET = AnyUrl(
    "https://gcmd.earthdata.nasa.gov/kms/concepts/concept_scheme/sciencekeywords"
)

DEFAULT_LANGUAGE: Language = Language(
    code="en-US", name="English (United States)", dir=None
)


def _to_datetime(value: date | datetime):
    if isinstance(value, datetime):
        return value if value.tzinfo else value.replace(tzinfo=timezone.utc)
    return datetime.combine(value, datetime.min.time(), tzinfo=timezone.utc)


def _to_contact(author: Person | AuthorRole) -> Contact1:
    position = None

    if isinstance(author, AuthorRole):
        position = author.role_name
        author = author.author

    return Contact1(
        identifier=str(author.identifier),
        name=f"{author.family_name}, {author.given_name}",
        organization=author.affiliation.name,
        position=position,
        emails=[Email(value=author.email)],
    )


class OgcRecordsTranspiler(Transpiler):
    def transpile(self, metadata_source: SoftwareApplication) -> Mapping[str, Any]:
        keywords: List[str] = []
        themes: List[Theme] = []

        if metadata_source.keywords:
            for raw_keyword in (
                metadata_source.keywords
                if isinstance(metadata_source.keywords, list)
                else [metadata_source.keywords]
            ):
                if isinstance(raw_keyword, str):
                    keywords.append(raw_keyword)
                elif isinstance(raw_keyword, DefinedTerm):
                    if (
                        SCIENCE_KEYWORDS_TERM_SET == raw_keyword.in_defined_term_set
                        and raw_keyword.term_code
                    ):
                        if raw_keyword.term_code not in KEYWORDS_INDEX:
                            logger.warning(
                                f"Science Keyword UUID {raw_keyword.term_code} not found in the index"
                            )
                        else:
                            science_keyword_record: ScienceKeywordRecord = (
                                KEYWORDS_INDEX[str(raw_keyword.term_code)]
                            )

                            concepts: List[Concept] = []

                            for i, keyword in enumerate(
                                science_keyword_record.hierarchy_list
                            ):
                                concepts.append(
                                    Concept(
                                        id=keyword,
                                        title=keyword,
                                        description=" > ".join(
                                            science_keyword_record.hierarchy_list[
                                                0 : i + 1
                                            ]
                                        ),
                                        url=science_keyword_record.uri,
                                    )
                                )

                            themes.append(
                                Theme(
                                    scheme=str(SCIENCE_KEYWORDS_TERM_SET),
                                    concepts=concepts,
                                )
                            )
                    else:
                        logger.debug(f"Discarding keyword, {raw_keyword}, unsupported")
                else:
                    logger.debug(
                        f"Discarding keyword, {raw_keyword}, unsupported type {type(raw_keyword)}"
                    )

        record_geojson: RecordGeoJSON = RecordGeoJSON(
            id=f"urn:uuid:{uuid.uuid4()}",
            type=Type7.FEATURE,
            links=list(
                map(
                    lambda creative_work: Link(
                        href=str(creative_work.url),
                        hreflang="en",
                        title=creative_work.name,
                        rel="help",
                        created=_to_datetime(metadata_source.date_created),
                        updated=_to_datetime(datetime.fromtimestamp(time.time())),
                    ),
                    metadata_source.software_help
                    if isinstance(metadata_source.software_help, list)
                    else [metadata_source.software_help],
                )
            ),
            properties=RecordCommonProperties(
                created=_to_datetime(metadata_source.date_created),
                updated=_to_datetime(datetime.fromtimestamp(time.time())),
                title=metadata_source.name,
                description=metadata_source.description
                if metadata_source.description
                else None,
                keywords=keywords if keywords else None,
                themes=themes if themes else None,
                language=DEFAULT_LANGUAGE,
                resource_languages=[DEFAULT_LANGUAGE],
                formats=[Format1(name="CWL", media_type="application/cwl")],
                contacts=list(
                    map(
                        _to_contact,
                        metadata_source.author
                        if isinstance(metadata_source.author, list)
                        else [metadata_source.author],
                    )
                ),
                license=": ".join(
                    list(
                        map(
                            lambda license: (
                                str(license.identifier)
                                if isinstance(license, CreativeWork)
                                else str(license)
                            ),
                            metadata_source.license
                            if isinstance(metadata_source.license, list)
                            else [metadata_source.license],
                        )
                    )
                ),
            ),
        )

        return record_geojson.model_dump(by_alias=True, exclude_none=True)
