from __future__ import annotations

from typing import Dict

from xdatawork.artifact.artifact import Artifact
from xdatawork.connect.connectlike import ConnectLike
from xdatawork.connect.connectref import ConnectRef
from xdatawork.connect.connectreflike import ConnectRefLike
from xdatawork.interface.kind.interface import InterfaceKind
from xdatawork.interface.kind.partition import PartitionKind


class BaseInterface:
    def __init__(
        self,
        ref: ConnectRefLike,
        connect: ConnectLike | None = None,
        parent: BaseInterface | None = None,
        kind: InterfaceKind | str | None = None,
        partitionkind: PartitionKind | str | None = None,
    ) -> None:
        self.parent: BaseInterface | None = self._resolve_parent(parent)
        self.kind: InterfaceKind = self._resolve_kind(kind)
        self.partitionkind: PartitionKind = self._resolve_partitionkind(partitionkind)
        self.ref: ConnectRefLike = self._resolve_ref(ref)
        self.connect: ConnectLike = self._resolve_connect(connect)

    def _resolve_parent(
        self,
        parent: BaseInterface | None,
    ) -> BaseInterface | None:
        if parent is None:
            return None
        if not isinstance(parent, BaseInterface):
            msg = f"Invalid parent: {type(parent)}, expected BaseInterface or None"
            raise TypeError(msg)
        return parent

    def _resolve_kind(
        self,
        kind: InterfaceKind | str | None,
    ) -> InterfaceKind:
        if kind is None:
            if self.parent is not None:
                return self.parent.kind
            else:
                return InterfaceKind.BaseInterface
        else:
            kind = InterfaceKind.from_any(kind)
            if kind not in InterfaceKind:
                msg = f"Invalid kind: {kind}, expected one of {list(InterfaceKind)}"
                raise ValueError(msg)
            else:
                return kind

    def _resolve_partitionkind(
        self,
        partitionkind: PartitionKind | str | None,
    ) -> PartitionKind:
        if partitionkind is None:
            if self.parent is not None:
                return self.parent.partitionkind
            else:
                return PartitionKind.BaseInterface
        else:
            partitionkind = PartitionKind.from_any(partitionkind)
            if partitionkind not in PartitionKind:
                msg = f"Invalid partitionkind: {partitionkind}, expected one of {list(PartitionKind)}"
                raise ValueError(msg)
            else:
                return partitionkind

    def _resolve_ref(
        self,
        ref: ConnectRefLike | Dict[str, str] | None,
    ) -> ConnectRefLike:
        if ref is None:
            raise ValueError("ref cannot be None")
        else:
            if isinstance(ref, ConnectRef):
                return ref
            if isinstance(ref, dict):
                return ConnectRef.from_dict(ref)
            msg = f"Invalid ref: {type(ref)}, expected ConnectRefLike or dict"
            raise TypeError(msg)

    def _resolve_connect(
        self,
        connect: ConnectLike | None,
    ) -> ConnectLike:
        if connect is None:
            if self.parent is not None:
                return self.parent.connect
            else:
                msg = "connect cannot be None if parent is None"
                raise ValueError(msg)
        if not isinstance(connect, ConnectLike):
            msg = f"Invalid connect: {type(connect)}, expected ConnectLike"
            raise TypeError(msg)
        return connect

    def list_refs(
        self,
        level: int | None = None,
        pattern: str | None = None,
    ) -> list[ConnectRefLike]:
        connectrefs = self.connect.list_objects(
            location=self.ref.location,
            level=level,
            pattern=pattern,
        )
        return connectrefs

    def list_artifacts(
        self,
        level: int | None = None,
        pattern: str | None = None,
    ) -> list[Artifact]:
        artifacts = []
        connectrefs: list[ConnectRefLike] = self.list_refs(
            level=level,
            pattern=pattern,
        )
        for connectref in connectrefs:
            artifact = Artifact(
                ref=connectref,
                connect=self.connect,
            )
            artifacts.append(artifact)
        return artifacts
