from __future__ import annotations

from collections.abc import Mapping, Sequence
from dataclasses import dataclass, field
from datetime import datetime
from typing import Literal, Protocol, runtime_checkable


ORGANIZATIONS_MODULE_ID = "organizations"
CAPABILITY_ORGANIZATION_DIRECTORY = "organizations.directory"
CAPABILITY_ORGANIZATION_HIERARCHY_DIRECTORY = (
    "organizations.hierarchyDirectory"
)

OrganizationStatus = Literal["active", "inactive", "suspended"]
OrganizationResolutionStatus = Literal[
    "active",
    "inactive",
    "missing",
    "unreachable",
    "invalid",
]
OrganizationHierarchyDirection = Literal["ancestors", "descendants"]
OrganizationLifecycleResource = Literal[
    "unit_type",
    "structure",
    "relation_type",
    "unit",
    "relation",
    "function_type",
    "function",
]
OrganizationLifecycleAction = Literal[
    "created",
    "updated",
    "moved",
    "deactivated",
]
ORGANIZATION_LIFECYCLE_EVENT_SCHEMA_VERSION = 1
ORGANIZATION_LIFECYCLE_RESOURCES: tuple[
    OrganizationLifecycleResource,
    ...,
] = (
    "unit_type",
    "structure",
    "relation_type",
    "unit",
    "relation",
    "function_type",
    "function",
)
ORGANIZATION_LIFECYCLE_ACTIONS: tuple[
    OrganizationLifecycleAction,
    ...,
] = ("created", "updated", "moved", "deactivated")


@dataclass(frozen=True, slots=True)
class OrganizationUnitRef:
    id: str
    tenant_id: str
    slug: str
    name: str
    unit_type_id: str | None = None
    parent_id: str | None = None
    description: str | None = None
    status: OrganizationStatus = "active"


@dataclass(frozen=True, slots=True)
class OrganizationFunctionRef:
    id: str
    tenant_id: str
    organization_unit_id: str
    slug: str
    name: str
    function_type_id: str | None = None
    description: str | None = None
    delegable: bool = False
    act_in_place_allowed: bool = False
    status: OrganizationStatus = "active"
    settings: Mapping[str, object] = field(default_factory=dict)


@dataclass(frozen=True, slots=True)
class OrganizationUnitTypeRef:
    id: str
    tenant_id: str
    slug: str
    name: str
    description: str | None = None
    status: OrganizationStatus = "active"


@dataclass(frozen=True, slots=True)
class OrganizationFunctionTypeRef:
    id: str
    tenant_id: str
    slug: str
    name: str
    organization_unit_type_id: str | None = None
    description: str | None = None
    delegable: bool = False
    act_in_place_allowed: bool = False
    status: OrganizationStatus = "active"
    settings: Mapping[str, object] = field(default_factory=dict)


@dataclass(frozen=True, slots=True)
class OrganizationStructureRef:
    id: str
    tenant_id: str
    slug: str
    name: str
    structure_kind: str
    description: str | None = None
    status: OrganizationStatus = "active"


@dataclass(frozen=True, slots=True)
class OrganizationRelationTypeRef:
    id: str
    tenant_id: str
    slug: str
    name: str
    structure_id: str | None = None
    source_unit_type_id: str | None = None
    target_unit_type_id: str | None = None
    is_hierarchical: bool = True
    allow_cycles: bool = False
    description: str | None = None
    status: OrganizationStatus = "active"


@dataclass(frozen=True, slots=True)
class OrganizationHierarchyCatalogRef:
    tenant_id: str
    structures: tuple[OrganizationStructureRef, ...] = ()
    relation_types: tuple[OrganizationRelationTypeRef, ...] = ()


@dataclass(frozen=True, slots=True)
class OrganizationHierarchyEdgeRef:
    id: str
    tenant_id: str
    structure: OrganizationStructureRef
    relation_type: OrganizationRelationTypeRef
    source_unit_id: str
    target_unit_id: str
    valid_from: datetime | None = None
    valid_until: datetime | None = None
    status: OrganizationStatus = "active"


@dataclass(frozen=True, slots=True)
class OrganizationHierarchyMatchRef:
    unit: OrganizationUnitRef
    depth: int
    path: tuple[OrganizationHierarchyEdgeRef, ...]


@dataclass(frozen=True, slots=True)
class OrganizationHierarchyResolution:
    tenant_id: str
    root_unit_id: str
    direction: OrganizationHierarchyDirection
    structure_id: str
    relation_type_ids: tuple[str, ...]
    max_depth: int
    status: OrganizationResolutionStatus
    root: OrganizationUnitRef | None = None
    matches: tuple[OrganizationHierarchyMatchRef, ...] = ()
    cycle_detected: bool = False
    depth_limited: bool = False
    diagnostics: tuple[str, ...] = ()


@dataclass(frozen=True, slots=True)
class OrganizationHierarchyPathResolution:
    tenant_id: str
    source_unit_id: str
    target_unit_id: str
    direction: OrganizationHierarchyDirection
    structure_id: str
    relation_type_ids: tuple[str, ...]
    max_depth: int
    status: OrganizationResolutionStatus
    source: OrganizationUnitRef | None = None
    target: OrganizationUnitRef | None = None
    path: tuple[OrganizationHierarchyEdgeRef, ...] = ()
    cycle_detected: bool = False
    depth_limited: bool = False
    diagnostics: tuple[str, ...] = ()


@dataclass(frozen=True, slots=True)
class OrganizationFunctionTypeResolution:
    tenant_id: str
    function_type_id: str
    requested_unit_ids: tuple[str, ...]
    status: OrganizationResolutionStatus
    function_type: OrganizationFunctionTypeRef | None = None
    matches: tuple[OrganizationFunctionRef, ...] = ()
    missing_unit_ids: tuple[str, ...] = ()
    inactive_unit_ids: tuple[str, ...] = ()
    diagnostics: tuple[str, ...] = ()


@dataclass(frozen=True, slots=True)
class OrganizationUnitTypeResolution:
    tenant_id: str
    unit_type_id: str
    status: OrganizationResolutionStatus
    unit_type: OrganizationUnitTypeRef | None = None
    structure_id: str | None = None
    root_unit_id: str | None = None
    matches: tuple[OrganizationUnitRef, ...] = ()
    diagnostics: tuple[str, ...] = ()


@runtime_checkable
class OrganizationDirectory(Protocol):
    def get_organization_unit(self, organization_unit_id: str) -> OrganizationUnitRef | None:
        ...

    def organization_units_for_tenant(self, tenant_id: str) -> Sequence[OrganizationUnitRef]:
        ...

    def get_function(self, function_id: str) -> OrganizationFunctionRef | None:
        ...

    def functions_for_organization_unit(
        self,
        organization_unit_id: str,
        *,
        include_subunits: bool = False,
    ) -> Sequence[OrganizationFunctionRef]:
        ...


@runtime_checkable
class OrganizationHierarchyDirectory(Protocol):
    def hierarchy_catalog(
        self,
        tenant_id: str,
    ) -> OrganizationHierarchyCatalogRef:
        ...

    def get_unit_type(
        self,
        tenant_id: str,
        unit_type_id: str,
    ) -> OrganizationUnitTypeRef | None:
        ...

    def get_function_type(
        self,
        tenant_id: str,
        function_type_id: str,
    ) -> OrganizationFunctionTypeRef | None:
        ...

    def resolve_functions_by_type(
        self,
        tenant_id: str,
        function_type_id: str,
        *,
        organization_unit_ids: Sequence[str] = (),
    ) -> OrganizationFunctionTypeResolution:
        ...

    def resolve_units_by_type(
        self,
        tenant_id: str,
        unit_type_id: str,
        *,
        structure_id: str | None = None,
        root_unit_id: str | None = None,
        relation_type_ids: Sequence[str] = (),
        direction: OrganizationHierarchyDirection = "descendants",
        max_depth: int = 10,
    ) -> OrganizationUnitTypeResolution:
        ...

    def resolve_hierarchy_relatives(
        self,
        tenant_id: str,
        organization_unit_ids: Sequence[str],
        *,
        structure_id: str,
        relation_type_ids: Sequence[str] = (),
        direction: OrganizationHierarchyDirection = "ancestors",
        max_depth: int = 10,
    ) -> Sequence[OrganizationHierarchyResolution]:
        ...

    def resolve_hierarchy_paths(
        self,
        tenant_id: str,
        unit_pairs: Sequence[tuple[str, str]],
        *,
        structure_id: str,
        relation_type_ids: Sequence[str] = (),
        direction: OrganizationHierarchyDirection = "descendants",
        max_depth: int = 10,
    ) -> Sequence[OrganizationHierarchyPathResolution]:
        ...


def organization_directory(
    registry: object | None,
) -> OrganizationDirectory | None:
    capability = _capability(registry, CAPABILITY_ORGANIZATION_DIRECTORY)
    return capability if isinstance(capability, OrganizationDirectory) else None


def organization_hierarchy_directory(
    registry: object | None,
) -> OrganizationHierarchyDirectory | None:
    capability = _capability(
        registry,
        CAPABILITY_ORGANIZATION_HIERARCHY_DIRECTORY,
    )
    return (
        capability
        if isinstance(capability, OrganizationHierarchyDirectory)
        else None
    )


def organization_lifecycle_event_type(
    resource: OrganizationLifecycleResource,
    action: OrganizationLifecycleAction,
) -> str:
    if resource not in ORGANIZATION_LIFECYCLE_RESOURCES:
        raise ValueError("Unsupported organization lifecycle resource.")
    if action not in ORGANIZATION_LIFECYCLE_ACTIONS:
        raise ValueError("Unsupported organization lifecycle action.")
    return f"organizations.{resource}.{action}.v1"


ORGANIZATION_DIRECTORY_INVALIDATION_EVENT_TYPES = frozenset(
    organization_lifecycle_event_type(resource, action)
    for resource in ORGANIZATION_LIFECYCLE_RESOURCES
    for action in ORGANIZATION_LIFECYCLE_ACTIONS
)


def _capability(registry: object | None, name: str) -> object | None:
    if (
        registry is None
        or not hasattr(registry, "has_capability")
        or not hasattr(registry, "capability")
        or not registry.has_capability(name)
    ):
        return None
    return registry.capability(name)


__all__ = [
    "CAPABILITY_ORGANIZATION_DIRECTORY",
    "CAPABILITY_ORGANIZATION_HIERARCHY_DIRECTORY",
    "ORGANIZATION_DIRECTORY_INVALIDATION_EVENT_TYPES",
    "ORGANIZATION_LIFECYCLE_ACTIONS",
    "ORGANIZATION_LIFECYCLE_EVENT_SCHEMA_VERSION",
    "ORGANIZATION_LIFECYCLE_RESOURCES",
    "ORGANIZATIONS_MODULE_ID",
    "OrganizationDirectory",
    "OrganizationFunctionRef",
    "OrganizationFunctionTypeRef",
    "OrganizationFunctionTypeResolution",
    "OrganizationHierarchyDirection",
    "OrganizationHierarchyCatalogRef",
    "OrganizationHierarchyDirectory",
    "OrganizationHierarchyEdgeRef",
    "OrganizationHierarchyMatchRef",
    "OrganizationHierarchyPathResolution",
    "OrganizationHierarchyResolution",
    "OrganizationLifecycleAction",
    "OrganizationLifecycleResource",
    "OrganizationRelationTypeRef",
    "OrganizationResolutionStatus",
    "OrganizationStatus",
    "OrganizationStructureRef",
    "OrganizationUnitRef",
    "OrganizationUnitTypeRef",
    "OrganizationUnitTypeResolution",
    "organization_directory",
    "organization_hierarchy_directory",
    "organization_lifecycle_event_type",
]
