from typing import TYPE_CHECKING, Any, ClassVar, Dict, Literal, Optional, cast

from pydantic import model_validator

from .._internal.common_nodes import InvalidDescr
from .._internal.io import FileDescr as FileDescr
from .._internal.io_basics import Sha256 as Sha256
from .._internal.types import FAIR
from .._internal.url import HttpUrl as HttpUrl
from ..generic.v0_3 import VALID_COVER_IMAGE_EXTENSIONS as VALID_COVER_IMAGE_EXTENSIONS
from ..generic.v0_3 import Author as Author
from ..generic.v0_3 import BadgeDescr as BadgeDescr
from ..generic.v0_3 import BioimageioConfig as BioimageioConfig
from ..generic.v0_3 import CiteEntry as CiteEntry
from ..generic.v0_3 import Config as Config
from ..generic.v0_3 import DeprecatedLicenseId as DeprecatedLicenseId
from ..generic.v0_3 import Doi as Doi
from ..generic.v0_3 import (
    GenericDescrBase,
    LinkedResourceBase,
    _author_conv,  # pyright: ignore[reportPrivateUsage]
    _maintainer_conv,  # pyright: ignore[reportPrivateUsage]
)
from ..generic.v0_3 import LicenseId as LicenseId
from ..generic.v0_3 import LinkedResource as LinkedResource
from ..generic.v0_3 import Maintainer as Maintainer
from ..generic.v0_3 import OrcidId as OrcidId
from ..generic.v0_3 import RelativeFilePath as RelativeFilePath
from ..generic.v0_3 import ResourceId as ResourceId
from ..generic.v0_3 import Uploader as Uploader
from ..generic.v0_3 import Version as Version
from .v0_2 import DatasetDescr as DatasetDescr02


class DatasetId(ResourceId):
    pass


class DatasetDescr(GenericDescrBase):
    """A bioimage.io dataset resource description file (dataset RDF) describes a dataset relevant to bioimage
    processing.
    """

    implemented_type: ClassVar[Literal["dataset"]] = "dataset"
    if TYPE_CHECKING:
        type: Literal["dataset"] = "dataset"
    else:
        type: Literal["dataset"]

    id: Optional[DatasetId] = None
    """bioimage.io-wide unique resource identifier
    assigned by bioimage.io; version **un**specific."""

    parent: Optional[DatasetId] = None
    """The description from which this one is derived"""

    source: FAIR[Optional[HttpUrl]] = None
    """"URL to the source of the dataset."""

    @model_validator(mode="before")
    @classmethod
    def _convert(cls, data: Dict[str, Any], /) -> Dict[str, Any]:
        if (
            data.get("type") == "dataset"
            and isinstance(fv := data.get("format_version"), str)
            and fv.startswith("0.2.")
        ):
            old = DatasetDescr02.load(data)
            if isinstance(old, InvalidDescr):
                return data

            return cast(
                Dict[str, Any],
                (cls if TYPE_CHECKING else dict)(
                    attachments=(
                        []
                        if old.attachments is None
                        else [FileDescr(source=f) for f in old.attachments.files]
                    ),
                    authors=[_author_conv.convert_as_dict(a) for a in old.authors],  # pyright: ignore[reportArgumentType]
                    badges=old.badges,
                    cite=[
                        {"text": c.text, "doi": c.doi, "url": c.url} for c in old.cite
                    ],  # pyright: ignore[reportArgumentType]
                    config=old.config,  # pyright: ignore[reportArgumentType]
                    covers=old.covers,
                    description=old.description,
                    documentation=old.documentation,
                    format_version="0.3.0",
                    git_repo=old.git_repo,  # pyright: ignore[reportArgumentType]
                    icon=old.icon,
                    id=None if old.id is None else DatasetId(old.id),
                    license=old.license,  # type: ignore
                    links=old.links,
                    maintainers=[
                        _maintainer_conv.convert_as_dict(m) for m in old.maintainers
                    ],  # pyright: ignore[reportArgumentType]
                    name=old.name,
                    source=old.source,
                    tags=old.tags,
                    type=old.type,
                    uploader=old.uploader,
                    version=old.version,
                    **(old.model_extra or {}),
                ),
            )

        return data


class LinkedDataset(LinkedResourceBase):
    """Reference to a bioimage.io dataset."""

    id: DatasetId
    """A valid dataset `id` from the bioimage.io collection."""
