//! Port traits for infrastructure management
//!
//! This module defines the port interfaces for hexagonal architecture,
//! allowing the core business logic to be independent of infrastructure details.

use crate::domain::*;
use async_trait::async_trait;
use thiserror::Error;

/// Errors that can occur during provisioning
#[derive(Error, Debug)]
pub enum ProvisionError {
    #[error("Connection error: {0}")]
    ConnectionError(String),

    #[error("Invalid specification: {0}")]
    InvalidSpec(String),

    #[error("Resource not found: {0}")]
    NotFound(String),

    #[error("Provisioning failed: {0}")]
    ProvisionFailed(String),

    #[error("Internal error: {0}")]
    Internal(String),
}

/// Port for provisioning infrastructure resources
#[async_trait]
pub trait InfrastructureProvisioner: Send + Sync {
    /// Provision complete infrastructure based on specification
    async fn provision(
        &self,
        spec: &InfrastructureSpec,
        dry_run: bool,
    ) -> Result<(), ProvisionError>;

    /// Apply infrastructure changes (create + update)
    async fn apply(
        &self,
        spec: &InfrastructureSpec,
        diff: &InfrastructureDiff,
    ) -> Result<(), ProvisionError>;

    /// Destroy complete infrastructure
    async fn destroy(&self, force: bool) -> Result<(), ProvisionError>;

    /// Get current infrastructure state
    async fn get_state(&self) -> Result<InfrastructureState, ProvisionError>;

    /// Validate infrastructure configuration
    async fn validate(&self, spec: &InfrastructureSpec) -> Result<Vec<String>, ProvisionError>;

    /// Provision individual server
    async fn provision_server(&self, spec: &ServerSpec) -> Result<Server, ProvisionError>;

    /// Update individual server
    async fn update_server(&self, spec: &ServerSpec) -> Result<Server, ProvisionError>;

    /// Provision network
    async fn provision_network(&self, spec: &NetworkSpec) -> Result<Network, ProvisionError>;

    /// Provision firewall
    async fn provision_firewall(&self, spec: &FirewallSpec) -> Result<Firewall, ProvisionError>;

    /// Update firewall
    async fn update_firewall(&self, spec: &FirewallSpec) -> Result<Firewall, ProvisionError>;

    /// Provision load balancer
    async fn provision_load_balancer(
        &self,
        spec: &LoadBalancerSpec,
    ) -> Result<LoadBalancer, ProvisionError>;
}

/// Errors that can occur during diffing
#[derive(Error, Debug)]
pub enum DifferError {
    #[error("Connection error: {0}")]
    ConnectionError(String),

    #[error("Invalid specification: {0}")]
    InvalidSpec(String),

    #[error("Internal error: {0}")]
    Internal(String),
}

/// Port for comparing infrastructure state
#[async_trait]
pub trait InfrastructureDiffer: Send + Sync {
    /// Compare desired specification with actual state
    async fn diff(&self, spec: &InfrastructureSpec) -> Result<InfrastructureDiff, DifferError>;
}
