"""Impact Assessment-related schemas."""

import datetime
from typing import Any
from uuid import UUID
from pydantic import AliasChoices, Field
from .common import BaseResponse, BaseSchema


# ---------------------------------------------------------------------------
# Graph expansion / query schemas
# ---------------------------------------------------------------------------


class DependencyNodeExpansionState(BaseSchema):
    """Metadata about unexplored connections for a graph node."""

    has_incoming: bool = Field(
        ..., description="Node has any incoming edges in the stored graph"
    )
    has_outgoing: bool = Field(
        ..., description="Node has any outgoing edges in the stored graph"
    )
    incoming_count: int = Field(
        ..., description="Total incoming edges in the stored graph"
    )
    outgoing_count: int = Field(
        ..., description="Total outgoing edges in the stored graph"
    )
    is_incoming_truncated: bool = Field(
        ..., description="More incoming edges exist beyond what was loaded"
    )
    is_outgoing_truncated: bool = Field(
        ..., description="More outgoing edges exist beyond what was loaded"
    )


class DependencyGraphPageInfo(BaseSchema):
    """Pagination / limit metadata for a dependency graph query."""

    applied_limit: int | None = Field(
        default=None, description="Relationship limit that was applied"
    )
    data_limit_mode: str = Field(default="same_parent", description="Limit mode used")
    was_truncated: bool = Field(
        default=False, description="Graph was truncated by any limit"
    )
    omitted_by_limit: bool = Field(
        default=False, description="Edges were omitted due to relationship_limit"
    )


# ---------------------------------------------------------------------------
# Graph expand request / response schemas
# ---------------------------------------------------------------------------


class DependencyGraphExpandNodeRequest(BaseSchema):
    """Request to expand a single node in the dependency graph."""

    item_id: str = Field(..., description="Item UUID of the node to expand")
    direction: str = Field(
        default="both",
        description="Edge direction to expand: 'incoming', 'outgoing', or 'both'",
    )
    limit: int | None = Field(default=None, gt=0, description="Max edges to return")
    cursor: str | None = Field(
        default=None, description="Pagination cursor from a previous expand"
    )


class DependencyGraphExpandRequest(BaseSchema):
    """Request body for the graph expand endpoint."""

    expand: list[DependencyGraphExpandNodeRequest] = Field(
        default_factory=list, description="Nodes to expand"
    )
    known_node_ids: list[str] = Field(
        default_factory=list,
        description="Node item_ids already held by the client (excluded from delta)",
    )
    known_edge_ids: list[str] = Field(
        default_factory=list,
        description="Edge IDs already held by the client (format 'source:target')",
    )


class DependencyGraphExpandContext(BaseSchema):
    """Context returned alongside an expand delta."""

    alignment_id: str = Field(..., description="Alignment the graph belongs to")
    expanded_node_ids: list[str] = Field(
        default_factory=list, description="Node IDs that were expanded"
    )
    next_cursors: dict[str, str | None] = Field(
        default_factory=dict, description="Next pagination cursor per expanded node ID"
    )


class DependencyGraphExpandDelta(BaseSchema):
    """Incremental graph delta returned by the expand endpoint."""

    added_nodes: list["DependencyNodeResponse"] = Field(
        default_factory=list, description="New nodes not previously known to the client"
    )
    added_edges: list["DependencyEdgeResponse"] = Field(
        default_factory=list, description="New edges not previously known to the client"
    )


class DependencyGraphExpandResponse(BaseSchema):
    """Response from the graph expand endpoint."""

    context: DependencyGraphExpandContext
    delta: DependencyGraphExpandDelta


class ImpactAssessmentResponse(BaseResponse):
    """Response schema for impact assessment data."""

    id: int = Field(..., description="Unique identifier for the assessment")
    finding_id: UUID = Field(..., description="ID of the associated finding")
    created_at: datetime.datetime = Field(
        ...,
        description="Creation timestamp",
        validation_alias=AliasChoices("timestamp", "createdAt", "created_at"),
    )


# Dependency Graph schemas
class DependencyNodeResponse(BaseSchema):
    """Response schema for a dependency graph node."""

    item_type: str = Field(..., description="Item type UUID")
    version: str = Field(..., description="Item type version")
    item_id: str = Field(..., description="Item UUID")
    is_root: bool = Field(
        ...,
        description="Whether this node is a root node directly associated with a finding",
    )
    item_data: dict[str, Any] | None = Field(
        default=None,
        description="Additional node data",
    )
    expansion_state: DependencyNodeExpansionState | None = Field(
        default=None,
        description="Expansion metadata indicating unexplored connections",
    )


class DependencyEdgeResponse(BaseSchema):
    """Response schema for a dependency graph edge."""

    source_item_id: str = Field(..., description="Source item UUID")
    target_item_id: str = Field(..., description="Target item UUID")
    status: str | None = Field(None, description="Edge status (e.g. sibling)")
    edge_id: str | None = Field(
        default=None,
        description="Stable edge ID (format 'source_item_id:target_item_id')",
    )


class DependencyGraphResponse(BaseSchema):
    """Response schema for a dependency graph."""

    nodes: list[DependencyNodeResponse] = Field(..., description="List of graph nodes")
    edges: list[DependencyEdgeResponse] = Field(..., description="List of graph edges")
    page_info: DependencyGraphPageInfo | None = Field(
        default=None,
        description="Limit and truncation metadata; present when relationship_limit is used",
    )


# Composite response schemas


class DependencySummaryEntry(BaseSchema):
    """Schema for summarizing dependencies by item type."""

    item_type: str = Field(..., description="Item type UUID")
    version: str = Field(..., description="Item type version")
    item_count: int = Field(..., description="Number of items impacted")


class DependencyItemTypeCountEntry(BaseSchema):
    """Schema for item counts grouped by item type."""

    item_type: str = Field(..., description="Item type UUID")
    item_count: int = Field(..., description="Number of projected items")


class ImpactAssessmentWithDependenciesResponse(BaseSchema):
    """Response schema for impact assessment with dependencies."""

    assessment: ImpactAssessmentResponse = Field(
        ..., description="Impact assessment data"
    )
    dependencies_summary: list[DependencySummaryEntry] | None = Field(
        default=None, description="List of dependencies"
    )
    dependencies_graph: DependencyGraphResponse | None = Field(
        None,
        description="Dependency graph showing relationships between impacted items",
    )


class FindingDependencyGraphRequest(BaseSchema):
    """Request schema for filtering finding dependency graphs."""

    finding_ids: list[UUID] | None = Field(
        None, description="Optional list of finding IDs to filter by"
    )


# Command Graph schemas
class CommandNodeResponse(BaseSchema):
    """Response schema for a command graph node."""

    command_id: str = Field(..., description="Command UUID")
    item_type_id: str = Field(..., description="Item type UUID")
    version: str = Field(..., description="Item type version")
    item_id: str = Field(..., description="Item UUID")
    data: dict[str, Any] | list[dict[str, Any]] = Field(
        default_factory=dict, description="Additional node data"
    )
    status: str | None = Field(..., description="Node status")


class CommandEdgeResponse(BaseSchema):
    """Response schema for a command graph edge."""

    source_command_id: str = Field(..., description="Source command UUID")
    source_item_id: str = Field(..., description="Source item UUID")
    target_command_id: str = Field(..., description="Target command UUID")
    target_item_id: str = Field(..., description="Target item UUID")
    status: str | None = Field(..., description="Edge status")


class CommandGraphResponse(BaseSchema):
    """Response schema for a command graph."""

    nodes: list[CommandNodeResponse] = Field(..., description="List of graph nodes")
    edges: list[CommandEdgeResponse] = Field(..., description="List of graph edges")
