//! System permission use cases.
//!
//! These use cases persist the wanted project/agent policies separately from
//! filesystem/bash permissions and resolve them through a read-only runtime
//! probe.

use std::sync::Arc;

use domain::ports::{RuntimePermissionProbe, SystemPermissionStore};
use domain::{
    resolve_agent_system_permissions, AgentId, Project, ProjectSystemPermissions,
    ResolvedAgentSystemPermissions, SystemPermissionSet,
};

use crate::error::AppError;

/// Reads the full project system permission document.
pub struct GetProjectSystemPermissions {
    store: Arc<dyn SystemPermissionStore>,
}

impl GetProjectSystemPermissions {
    /// Builds the use case.
    #[must_use]
    pub fn new(store: Arc<dyn SystemPermissionStore>) -> Self {
        Self { store }
    }

    /// Executes the read.
    pub async fn execute(
        &self,
        input: GetProjectSystemPermissionsInput,
    ) -> Result<GetProjectSystemPermissionsOutput, AppError> {
        let permissions = self.store.load_system_permissions(&input.project).await?;
        Ok(GetProjectSystemPermissionsOutput { permissions })
    }
}

/// Input for [`GetProjectSystemPermissions`].
pub struct GetProjectSystemPermissionsInput {
    /// Target project.
    pub project: Project,
}

/// Output for project system permission reads/mutations.
pub struct GetProjectSystemPermissionsOutput {
    /// Persisted system permission document.
    pub permissions: ProjectSystemPermissions,
}

/// Replaces the project default system permissions.
pub struct UpdateProjectSystemPermissions {
    store: Arc<dyn SystemPermissionStore>,
}

impl UpdateProjectSystemPermissions {
    /// Builds the use case.
    #[must_use]
    pub fn new(store: Arc<dyn SystemPermissionStore>) -> Self {
        Self { store }
    }

    /// Executes the mutation.
    pub async fn execute(
        &self,
        input: UpdateProjectSystemPermissionsInput,
    ) -> Result<GetProjectSystemPermissionsOutput, AppError> {
        let mut doc = self.store.load_system_permissions(&input.project).await?;
        doc.set_project_default(input.permissions);
        self.store
            .save_system_permissions(&input.project, &doc)
            .await?;
        Ok(GetProjectSystemPermissionsOutput { permissions: doc })
    }
}

/// Input for [`UpdateProjectSystemPermissions`].
pub struct UpdateProjectSystemPermissionsInput {
    /// Target project.
    pub project: Project,
    /// New project default policy. `None` removes project defaults.
    pub permissions: Option<SystemPermissionSet>,
}

/// Replaces one agent system permission override.
pub struct UpdateAgentSystemPermissions {
    store: Arc<dyn SystemPermissionStore>,
}

impl UpdateAgentSystemPermissions {
    /// Builds the use case.
    #[must_use]
    pub fn new(store: Arc<dyn SystemPermissionStore>) -> Self {
        Self { store }
    }

    /// Executes the mutation.
    pub async fn execute(
        &self,
        input: UpdateAgentSystemPermissionsInput,
    ) -> Result<GetProjectSystemPermissionsOutput, AppError> {
        let mut doc = self.store.load_system_permissions(&input.project).await?;
        doc.set_agent_permissions(input.agent_id, input.permissions);
        self.store
            .save_system_permissions(&input.project, &doc)
            .await?;
        Ok(GetProjectSystemPermissionsOutput { permissions: doc })
    }
}

/// Input for [`UpdateAgentSystemPermissions`].
pub struct UpdateAgentSystemPermissionsInput {
    /// Target project.
    pub project: Project,
    /// Target agent.
    pub agent_id: AgentId,
    /// New agent policy. `None` removes the override.
    pub permissions: Option<SystemPermissionSet>,
}

/// Resolves effective system permissions for one agent.
pub struct ResolveAgentSystemPermissions {
    store: Arc<dyn SystemPermissionStore>,
    runtime_probe: Arc<dyn RuntimePermissionProbe>,
}

impl ResolveAgentSystemPermissions {
    /// Builds the use case.
    #[must_use]
    pub fn new(
        store: Arc<dyn SystemPermissionStore>,
        runtime_probe: Arc<dyn RuntimePermissionProbe>,
    ) -> Self {
        Self {
            store,
            runtime_probe,
        }
    }

    /// Executes the resolution.
    pub async fn execute(
        &self,
        input: ResolveAgentSystemPermissionsInput,
    ) -> Result<ResolveAgentSystemPermissionsOutput, AppError> {
        let doc = self.store.load_system_permissions(&input.project).await?;
        let runtime = self
            .runtime_probe
            .probe_runtime_permissions(&input.project, input.agent_id)
            .await?;
        Ok(ResolveAgentSystemPermissionsOutput {
            permissions: resolve_agent_system_permissions(&doc, input.agent_id, runtime),
        })
    }
}

/// Input for [`ResolveAgentSystemPermissions`].
pub struct ResolveAgentSystemPermissionsInput {
    /// Target project.
    pub project: Project,
    /// Target agent.
    pub agent_id: AgentId,
}

/// Output for [`ResolveAgentSystemPermissions`].
pub struct ResolveAgentSystemPermissionsOutput {
    /// Resolved system permissions.
    pub permissions: ResolvedAgentSystemPermissions,
}
