from __future__ import annotations

from enum import Enum
from typing import Final, NotRequired, TypedDict

from omu.helper import map_optional
from omu.identifier import Identifier
from omu.interface import Keyable
from omu.localization import Locale, LocalizedText
from omu.model import Model


class AppMetadata(TypedDict):
    locale: Locale
    name: NotRequired[LocalizedText | None]
    icon: NotRequired[LocalizedText | None]
    description: NotRequired[LocalizedText | None]
    image: NotRequired[LocalizedText | None]
    site: NotRequired[LocalizedText | None]
    repository: NotRequired[LocalizedText | None]
    authors: NotRequired[LocalizedText | None]
    license: NotRequired[LocalizedText | None]
    tags: NotRequired[list[str] | None]


class AppType(Enum):
    APP = "app"
    SERVICE = "service"
    REMOTE = "remote"
    PLUGIN = "plugin"
    DASHBOARD = "dashboard"


class DependencySpecifier(TypedDict):
    version: str
    index: NotRequired[str] | None


class AppJson(TypedDict):
    id: str
    type: str
    parent_id: NotRequired[str | None]
    version: NotRequired[str | None]
    url: NotRequired[str | None]
    metadata: NotRequired[AppMetadata | None]
    dependencies: NotRequired[dict[str, DependencySpecifier | str] | None]


class App(Keyable, Model[AppJson]):
    def __init__(
        self,
        id: Identifier | str,
        *,
        version: str | None = None,
        url: str | None = None,
        type: AppType = AppType.APP,
        parent_id: Identifier | None = None,
        metadata: AppMetadata | None = None,
        dependencies: dict[str, DependencySpecifier | str] | None = None,
    ) -> None:
        if isinstance(id, str):
            id = Identifier.from_key(id)
        self.parent_id = parent_id
        self.id: Final[Identifier] = id
        self.version = version
        self.url = url
        self.type = type
        self.metadata = metadata
        self.dependencies = dependencies

    @classmethod
    def from_json(cls, json: AppJson) -> App:
        id = Identifier.from_key(json["id"])
        return cls(
            id,
            parent_id=map_optional(json.get("parent_id"), Identifier.from_key),
            version=json.get("version"),
            url=json.get("url"),
            type=AppType(json.get("type", None) or "app"),
            metadata=json.get("metadata"),
            dependencies=json.get("dependencies"),
        )

    def to_json(self) -> AppJson:
        return AppJson(
            id=self.key(),
            parent_id=self.parent_id.key() if self.parent_id else None,
            version=self.version,
            url=self.url,
            type=self.type.value,
            metadata=self.metadata,
            dependencies=self.dependencies,
        )

    def key(self) -> str:
        return self.id.key()

    def __hash__(self) -> int:
        return hash(self.key())

    def __repr__(self) -> str:
        return f"App({self.key()})"
