"""
Infrastructure model for hardware and virtual assets.

Infrastructure represents servers, network devices, workstations, and other
IT assets with detailed configuration and environmental constraints.
"""

from typing import TYPE_CHECKING, Optional

from sqlalchemy import Boolean, CHAR, CheckConstraint, ForeignKey, Index, String, Text
from sqlalchemy.orm import Mapped, mapped_column, relationship

from .base import Base, TimestampMixin, UUIDMixin

if TYPE_CHECKING:
    from .database_change import DatabaseChange
    from .deployment import Deployment
    from .infrastructure_change import InfrastructureChange


class Infrastructure(Base, UUIDMixin, TimestampMixin):
    """
    Infrastructure model representing IT assets.

    Tracks physical servers, virtual machines, containers, network devices,
    NAS storage, workstations, and other infrastructure components with
    detailed configuration and environmental constraints.

    Attributes:
        client_id: Reference to the client
        site_id: Reference to the site this infrastructure is located at
        asset_type: Type of asset (physical_server, virtual_machine, etc.)
        hostname: Hostname of the infrastructure
        ip_address: IP address (IPv4 or IPv6)
        mac_address: MAC address
        os: Operating system name
        os_version: Operating system version
        role_description: Description of the infrastructure's role
        parent_host_id: Reference to parent host for VMs/containers
        status: Current status (active, migration_source, etc.)
        environmental_notes: Special environmental constraints or notes
        powershell_version: PowerShell version if applicable
        shell_type: Shell type (bash, cmd, powershell, sh)
        package_manager: Package manager (apt, yum, chocolatey, none)
        has_gui: Whether the system has a GUI
        limitations: JSON array of limitations
        notes: Additional notes
    """

    __tablename__ = "infrastructure"

    # Foreign keys
    client_id: Mapped[Optional[str]] = mapped_column(
        CHAR(36),
        ForeignKey("clients.id", ondelete="CASCADE"),
        doc="Reference to the client"
    )

    site_id: Mapped[Optional[str]] = mapped_column(
        CHAR(36),
        ForeignKey("sites.id", ondelete="SET NULL"),
        doc="Reference to the site this infrastructure is located at"
    )

    # Asset identification
    asset_type: Mapped[str] = mapped_column(
        String(50),
        nullable=False,
        doc="Type: physical_server, virtual_machine, container, network_device, nas_storage, workstation, firewall, domain_controller"
    )

    hostname: Mapped[str] = mapped_column(
        String(255),
        nullable=False,
        doc="Hostname of the infrastructure"
    )

    ip_address: Mapped[Optional[str]] = mapped_column(
        String(45),
        doc="IP address (IPv4 or IPv6)"
    )

    mac_address: Mapped[Optional[str]] = mapped_column(
        String(17),
        doc="MAC address"
    )

    # Operating system
    os: Mapped[Optional[str]] = mapped_column(
        String(255),
        doc="Operating system name (e.g., 'Ubuntu 22.04', 'Windows Server 2022')"
    )

    os_version: Mapped[Optional[str]] = mapped_column(
        String(100),
        doc="Operating system version (e.g., '6.22', '2008 R2', '22.04')"
    )

    # Role and hierarchy
    role_description: Mapped[Optional[str]] = mapped_column(
        Text,
        doc="Description of the infrastructure's role"
    )

    parent_host_id: Mapped[Optional[str]] = mapped_column(
        CHAR(36),
        ForeignKey("infrastructure.id", ondelete="SET NULL"),
        doc="Reference to parent host for VMs/containers"
    )

    # Status
    status: Mapped[str] = mapped_column(
        String(50),
        default="active",
        server_default="active",
        nullable=False,
        doc="Status: active, migration_source, migration_destination, decommissioned"
    )

    # Environmental constraints
    environmental_notes: Mapped[Optional[str]] = mapped_column(
        Text,
        doc="Special environmental constraints or notes (e.g., 'Manual WINS install', 'ReadyNAS OS, SMB1 only')"
    )

    powershell_version: Mapped[Optional[str]] = mapped_column(
        String(20),
        doc="PowerShell version (e.g., '2.0', '5.1', '7.4')"
    )

    shell_type: Mapped[Optional[str]] = mapped_column(
        String(50),
        doc="Shell type: bash, cmd, powershell, sh"
    )

    package_manager: Mapped[Optional[str]] = mapped_column(
        String(50),
        doc="Package manager: apt, yum, chocolatey, none"
    )

    has_gui: Mapped[bool] = mapped_column(
        Boolean,
        default=True,
        server_default="1",
        nullable=False,
        doc="Whether the system has a GUI"
    )

    limitations: Mapped[Optional[str]] = mapped_column(
        Text,
        doc='JSON array of limitations (e.g., ["no_ps7", "smb1_only", "dos_6.22_commands"])'
    )

    # Notes
    notes: Mapped[Optional[str]] = mapped_column(
        Text,
        doc="Additional notes"
    )

    # Relationships
    deployments: Mapped[list["Deployment"]] = relationship(
        "Deployment",
        back_populates="infrastructure",
        doc="Relationship to Deployment model"
    )

    database_changes: Mapped[list["DatabaseChange"]] = relationship(
        "DatabaseChange",
        back_populates="infrastructure",
        doc="Relationship to DatabaseChange model"
    )

    infrastructure_changes: Mapped[list["InfrastructureChange"]] = relationship(
        "InfrastructureChange",
        back_populates="infrastructure",
        doc="Relationship to InfrastructureChange model"
    )

    # Constraints and indexes
    __table_args__ = (
        CheckConstraint(
            "asset_type IN ('physical_server', 'virtual_machine', 'container', 'network_device', 'nas_storage', 'workstation', 'firewall', 'domain_controller')",
            name="ck_infrastructure_asset_type"
        ),
        CheckConstraint(
            "status IN ('active', 'migration_source', 'migration_destination', 'decommissioned')",
            name="ck_infrastructure_status"
        ),
        Index("idx_infrastructure_client", "client_id"),
        Index("idx_infrastructure_type", "asset_type"),
        Index("idx_infrastructure_hostname", "hostname"),
        Index("idx_infrastructure_parent", "parent_host_id"),
        Index("idx_infrastructure_os", "os"),
    )

    def __repr__(self) -> str:
        """String representation of the infrastructure."""
        return f"<Infrastructure(hostname='{self.hostname}', asset_type='{self.asset_type}')>"
