//! Permission use cases.
//!
//! This module stays at the application boundary: it loads the project
//! permission document through [`PermissionStore`], applies simple mutations, and
//! delegates all merge semantics to the pure domain model.

use std::sync::Arc;

use domain::ports::PermissionStore;
use domain::{
    AgentId, EffectivePermissions, PermissionSet, PermissionShadowReport, Project,
    ProjectPermissions,
};

use crate::error::AppError;

/// Reads the full project permission document.
pub struct GetProjectPermissions {
    store: Arc<dyn PermissionStore>,
}

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

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

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

/// Output for [`GetProjectPermissions`].
pub struct GetProjectPermissionsOutput {
    /// Persisted permission document.
    pub permissions: ProjectPermissions,
}

/// Replaces the project default policy.
pub struct UpdateProjectPermissions {
    store: Arc<dyn PermissionStore>,
}

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

    /// Executes the mutation.
    pub async fn execute(
        &self,
        input: UpdateProjectPermissionsInput,
    ) -> Result<GetProjectPermissionsOutput, AppError> {
        let mut doc = self.store.load_permissions(&input.project).await?;
        doc.set_project_defaults(input.permissions);
        self.store.save_permissions(&input.project, &doc).await?;
        Ok(GetProjectPermissionsOutput { permissions: doc })
    }
}

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

/// Replaces one agent override.
pub struct UpdateAgentPermissions {
    store: Arc<dyn PermissionStore>,
}

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

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

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

/// Resolves effective permissions for one agent.
pub struct ResolveAgentPermissions {
    store: Arc<dyn PermissionStore>,
}

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

    /// Executes the resolution.
    pub async fn execute(
        &self,
        input: ResolveAgentPermissionsInput,
    ) -> Result<ResolveAgentPermissionsOutput, AppError> {
        let doc = self.store.load_permissions(&input.project).await?;
        Ok(ResolveAgentPermissionsOutput {
            effective: doc.resolve_for(input.agent_id),
            shadowed: doc.shadow_for(input.agent_id),
        })
    }
}

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

/// Output for [`ResolveAgentPermissions`].
pub struct ResolveAgentPermissionsOutput {
    /// Resolved policy, or `None` when neither project nor agent policy exists.
    pub effective: Option<EffectivePermissions>,
    /// Diagnostic report for agent-level allows shadowed by project defaults.
    pub shadowed: PermissionShadowReport,
}
