import uuid
from datetime import datetime
from typing import Any, Literal

from pydantic import BaseModel, ConfigDict, Field


MappingFit = Literal["direct", "partial", "conflict", "gap", "unknown"]
EvidenceState = Literal["observed", "declared", "inferred", "proposed", "gap"]
FabricGraphApiVersion = Literal["railiance.fabric/v1alpha1", "railiance.fabric/v1alpha2"]
ActorRole = Literal["king", "lord", "tenant", "operator", "steward"]
OwnershipResolution = Literal["explicit", "inherited", "unresolved", "ambiguous"]
EvidenceReviewState = Literal["accepted", "candidate", "needs_review", "rejected"]
RelationshipCategory = Literal[
    "containment",
    "ownership",
    "technical",
    "utility",
    "accounting",
    "evidence",
]


class FabricGraphSource(BaseModel):
    model_config = ConfigDict(extra="allow")

    repo: str | None = None
    producer: str | None = None
    registry: str | None = None
    commit: str | None = None
    path: str | None = None
    generation_reason: str | None = None


class FabricGraphNetkingdomPayload(BaseModel):
    model_config = ConfigDict(extra="forbid")

    id: str
    name: str
    king_actor_id: str


class FabricGraphActorPayload(BaseModel):
    model_config = ConfigDict(extra="allow")

    id: str
    kind: Literal["FabricActor"]
    role: ActorRole
    name: str


class FabricGraphFabricPayload(BaseModel):
    model_config = ConfigDict(extra="allow")

    id: str
    kind: Literal["Fabric", "Subfabric"]
    name: str
    netkingdom_id: str
    parent_fabric_id: str | None = None
    lord_actor_id: str | None = None
    tenant_actor_id: str | None = None
    status: str
    boundary: dict[str, Any] = Field(default_factory=dict)
    evidence_refs: list[dict[str, Any]] = Field(default_factory=list)


class FabricGraphContainmentPayload(BaseModel):
    model_config = ConfigDict(extra="forbid")

    netkingdom_id: str
    fabric_id: str
    subfabric_id: str | None = None
    environment: str | None = None
    deployment_scenario_id: str | None = None


class FabricGraphOwnershipPayload(BaseModel):
    model_config = ConfigDict(extra="forbid")

    owner_actor_id: str
    owner_role: ActorRole
    resolution: OwnershipResolution
    inherited_from: str | None = None
    supporting_actor_ids: list[str] = Field(default_factory=list)


class FabricGraphAccountingPayload(BaseModel):
    model_config = ConfigDict(extra="allow")

    cost_center_id: str | None = None
    profit_center_id: str | None = None
    provider_profit_center_id: str | None = None
    consumer_cost_center_id: str | None = None
    allocation_model: str | None = None
    payment_schema_id: str | None = None
    metering_basis: str | None = None
    valid_from: str | None = None
    valid_until: str | None = None


class FabricGraphEvidencePayload(BaseModel):
    model_config = ConfigDict(extra="forbid")

    state: EvidenceState
    review_state: EvidenceReviewState
    confidence: float | None = Field(default=None, ge=0, le=1)
    refs: list[dict[str, Any]] = Field(default_factory=list)


class FabricGraphUtilitySidePayload(BaseModel):
    model_config = ConfigDict(extra="forbid")

    owner_actor_id: str
    fabric_id: str
    subfabric_id: str | None = None


class FabricGraphBoundaryPayload(BaseModel):
    model_config = ConfigDict(extra="forbid")

    crosses_fabric_boundary: bool
    crosses_subfabric_boundary: bool


class FabricGraphUtilityPayload(BaseModel):
    model_config = ConfigDict(extra="allow")

    utility_type: str
    contract_id: str | None = None
    payment_schema_id: str | None = None
    metering_basis: str | None = None
    business_model: str | None = None


class FabricGraphUnresolvedPayload(BaseModel):
    model_config = ConfigDict(extra="allow")

    target_id: str
    kind: str
    severity: str | None = None
    message: str
    evidence_refs: list[dict[str, Any]] = Field(default_factory=list)


class FabricGraphNodePayload(BaseModel):
    model_config = ConfigDict(extra="forbid")

    id: str
    kind: str
    name: str
    repo: str | None = None
    domain: str | None = None
    lifecycle: str | None = None
    containment: FabricGraphContainmentPayload | None = None
    ownership: FabricGraphOwnershipPayload | None = None
    accounting: FabricGraphAccountingPayload | None = None
    evidence: FabricGraphEvidencePayload | None = None
    canon_category: str | None = None
    canon_anchor: str | None = None
    mapping_fit: MappingFit | None = None
    evidence_state: EvidenceState | None = None
    attributes: dict[str, Any] = Field(default_factory=dict)


class FabricGraphEdgePayload(BaseModel):
    model_config = ConfigDict(extra="forbid", populate_by_name=True)

    from_graph_id: str = Field(alias="from")
    to_graph_id: str = Field(alias="to")
    edge_type: str = Field(alias="type")
    id: str | None = None
    relationship_category: RelationshipCategory | None = None
    canonical_type: str | None = None
    canon_anchor: str | None = None
    mapping_fit: MappingFit | None = None
    display_only: bool | None = None
    evidence_state: EvidenceState | None = None
    provider: FabricGraphUtilitySidePayload | None = None
    consumer: FabricGraphUtilitySidePayload | None = None
    boundary: FabricGraphBoundaryPayload | None = None
    utility: FabricGraphUtilityPayload | None = None
    accounting: FabricGraphAccountingPayload | None = None
    evidence: FabricGraphEvidencePayload | None = None
    attributes: dict[str, Any] = Field(default_factory=dict)


class FabricGraphExportPayload(BaseModel):
    model_config = ConfigDict(extra="forbid", populate_by_name=True)

    api_version: FabricGraphApiVersion = Field(alias="apiVersion")
    kind: Literal["FabricGraphExport"]
    schema_version: Literal["financial-fabric-v1"] | None = None
    generated_at: datetime | None = None
    source: FabricGraphSource | None = None
    compatibility: dict[str, Any] = Field(default_factory=dict)
    netkingdom: FabricGraphNetkingdomPayload | None = None
    actors: list[FabricGraphActorPayload] = Field(default_factory=list)
    fabrics: list[FabricGraphFabricPayload] = Field(default_factory=list)
    nodes: list[FabricGraphNodePayload]
    edges: list[FabricGraphEdgePayload]
    unresolved: list[FabricGraphUnresolvedPayload] = Field(default_factory=list)


class FabricGraphPullRequest(BaseModel):
    source_repo_slug: str = "railiance-fabric"
    source_url: str = "http://127.0.0.1:8765/exports/state-hub"
    requested_by: str = "operator"


class FabricGraphImportRead(BaseModel):
    model_config = ConfigDict(from_attributes=True)

    id: uuid.UUID
    source_repo_slug: str
    source_url: str | None = None
    source_commit: str | None = None
    source_path: str | None = None
    api_version: str | None = None
    schema_version: str | None = None
    export_kind: str | None = None
    exported_at: datetime | None = None
    netkingdom_id: str | None = None
    king_actor_id: str | None = None
    actor_count: int
    fabric_count: int
    unresolved_count: int
    compatibility: dict
    content_hash: str
    node_count: int
    edge_count: int
    validation_status: str
    error_details: dict | None = None
    is_latest: bool
    last_seen_at: datetime | None = None
    created_at: datetime
    updated_at: datetime


class FabricGraphIngestResult(BaseModel):
    import_run: FabricGraphImportRead
    created: bool
    idempotent: bool
    node_count: int
    edge_count: int


class FabricGraphNodeRead(BaseModel):
    model_config = ConfigDict(from_attributes=True)

    id: uuid.UUID
    import_id: uuid.UUID
    source_repo_slug: str
    graph_id: str
    kind: str
    name: str
    repo_slug: str
    domain_slug: str
    lifecycle: str
    canonical_type: str | None = None
    canon_category: str | None = None
    canon_anchor: str | None = None
    mapping_fit: str | None = None
    evidence_state: str | None = None
    evidence_review_state: str | None = None
    evidence_confidence: float | None = None
    netkingdom_id: str | None = None
    fabric_id: str | None = None
    subfabric_id: str | None = None
    environment: str | None = None
    deployment_scenario_id: str | None = None
    owner_actor_id: str | None = None
    owner_role: str | None = None
    ownership_resolution: str | None = None
    cost_center_id: str | None = None
    profit_center_id: str | None = None
    display_only: bool
    attributes: dict
    raw_json: dict


class FabricGraphEdgeRead(BaseModel):
    model_config = ConfigDict(from_attributes=True)

    id: uuid.UUID
    import_id: uuid.UUID
    source_repo_slug: str
    edge_key: str
    from_graph_id: str
    to_graph_id: str
    edge_type: str
    canonical_type: str | None = None
    canon_anchor: str | None = None
    mapping_fit: str | None = None
    evidence_state: str | None = None
    evidence_review_state: str | None = None
    evidence_confidence: float | None = None
    relationship_category: str | None = None
    provider_owner_actor_id: str | None = None
    provider_fabric_id: str | None = None
    provider_subfabric_id: str | None = None
    consumer_owner_actor_id: str | None = None
    consumer_fabric_id: str | None = None
    consumer_subfabric_id: str | None = None
    crosses_fabric_boundary: bool | None = None
    crosses_subfabric_boundary: bool | None = None
    utility_type: str | None = None
    utility_contract_id: str | None = None
    utility_payment_schema_id: str | None = None
    utility_metering_basis: str | None = None
    utility_business_model: str | None = None
    cost_center_id: str | None = None
    profit_center_id: str | None = None
    provider_profit_center_id: str | None = None
    consumer_cost_center_id: str | None = None
    display_only: bool
    attributes: dict
    raw_json: dict


class FabricGraphSummary(BaseModel):
    source_repo_slug: str
    latest_import: FabricGraphImportRead | None = None
    node_count: int
    edge_count: int
    schema_version: str | None = None
    netkingdom_id: str | None = None
    actor_count: int
    fabric_count: int
    unresolved_count: int
    nodes_by_domain: dict[str, int]
    nodes_by_repo: dict[str, int]
    nodes_by_canon_category: dict[str, int]
    nodes_by_fabric: dict[str, int]
    nodes_by_subfabric: dict[str, int]
    nodes_by_owner_actor: dict[str, int]
    nodes_by_owner_role: dict[str, int]
    nodes_by_ownership_resolution: dict[str, int]
    edges_by_canonical_type: dict[str, int]
    edges_by_relationship_category: dict[str, int]
    utility_edges_by_provider_owner: dict[str, int]
    utility_edges_by_consumer_owner: dict[str, int]
    utility_edges_by_business_model: dict[str, int]
    nodes_by_evidence_state: dict[str, int]
    edges_by_evidence_state: dict[str, int]
    nodes_by_mapping_fit: dict[str, int]
    edges_by_mapping_fit: dict[str, int]
    tenant_utilities_without_payment_schema: int
    nodes_without_accounting_attribution: int
    unresolved_ownership_count: int
    unresolved_accounting_count: int
    example_nodes: list[FabricGraphNodeRead]
    example_edges: list[FabricGraphEdgeRead]
