"""Pydantic models shared across modules."""

from __future__ import annotations

from datetime import datetime, timezone
from enum import Enum
from pathlib import Path
from typing import Any, Dict, List, Optional

from pydantic import BaseModel, Field, field_validator


class AzureEnvironment(str, Enum):
    """Supported Azure clouds."""

    AZURE_PUBLIC = "azure_public"
    AZURE_GOV = "azure_gov"
    AZURE_CHINA = "azure_china"
    AZURE_GERMANY = "azure_germany"
    AZURE_STACK = "azure_stack"


class AzureEnvironmentConfig(BaseModel):
    """Concrete endpoints for an Azure cloud."""

    name: AzureEnvironment
    authority_host: str
    resource_manager: str
    storage_suffix: str


class DiscoveryFilter(BaseModel):
    """Optional filters used to scope discovery."""

    include_types: Optional[List[str]] = None
    exclude_types: Optional[List[str]] = None
    required_tags: Optional[Dict[str, str]] = None
    resource_groups: Optional[List[str]] = None


class VisualizationOptions(BaseModel):
    """Graph visualization configuration."""

    output_dir: Path = Field(default=Path("artifacts/graphs"))
    file_name: Optional[str] = None
    include_attributes: bool = True
    physics_enabled: bool = True

    @field_validator("output_dir", mode="before")
    @classmethod
    def _ensure_path(cls, value: Any) -> Path:
        if value is None:
            raise ValueError("output_dir cannot be None")
        return Path(value)


class AzureDiscoveryRequest(BaseModel):
    """Input payload for discovery operations."""

    tenant_id: str
    environment: AzureEnvironment = AzureEnvironment.AZURE_PUBLIC
    subscriptions: Optional[List[str]] = None
    filter: Optional[DiscoveryFilter] = None
    include_relationships: bool = True
    max_batch_size: int = Field(default=1000, ge=100, le=5000)
    throttle_delay_seconds: float = Field(default=0.05, ge=0.0, le=5.0)
    prefer_cli_credentials: bool = False
    visualization: VisualizationOptions = Field(
        default_factory=VisualizationOptions
    )

    @field_validator("tenant_id")
    @classmethod
    def _validate_tenant(cls, value: str) -> str:
        if not value or not value.strip():
            raise ValueError("tenant_id is required")
        return value.strip()


class ResourceNode(BaseModel):
    """Normalized Azure resource."""

    id: str
    name: str
    type: str
    location: Optional[str] = None
    subscription_id: str
    resource_group: Optional[str] = None
    tags: Dict[str, str] = Field(default_factory=dict)
    dependencies: List[str] = Field(default_factory=list)


class ResourceRelationship(BaseModel):
    """Edge describing resource dependency."""

    source_id: str
    target_id: str
    relation_type: str
    weight: float = 1.0


class AzureDiscoveryResponse(BaseModel):
    """Discovery result payload."""

    tenant_id: str
    discovered_subscriptions: List[str]
    nodes: List[ResourceNode]
    relationships: List[ResourceRelationship]
    total_resources: int
    generated_at: datetime = Field(
        default_factory=lambda: datetime.now(timezone.utc)
    )
    html_report_path: Optional[str] = None


class VisualizationResponse(BaseModel):
    """Result of graph rendering."""

    html_path: str
    nodes: int
    relationships: int
