use super::*;

impl AuthStore {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn upsert_tenant(&mut self, tenant: Tenant) {
        self.tenants.insert(tenant.id.clone(), tenant);
    }

    pub fn create_tenant(&mut self, tenant_id: impl Into<String>) {
        self.upsert_tenant(Tenant::active(tenant_id));
    }

    pub fn tenant_count(&self) -> usize {
        self.tenants.len()
    }

    pub fn principal_console_rows(&self) -> Vec<(String, String, String, bool, usize)> {
        let mut rows = self
            .principals
            .values()
            .map(|principal| {
                let role_count = self
                    .role_assignments
                    .get(&principal.id)
                    .map(Vec::len)
                    .unwrap_or_default();
                (
                    principal.id.clone(),
                    principal.tenant_id.clone(),
                    format!("{:?}", principal.kind),
                    principal.enabled,
                    role_count,
                )
            })
            .collect::<Vec<_>>();
        rows.sort_by(|left, right| left.0.cmp(&right.0));
        rows
    }

    pub fn upsert_principal(&mut self, principal: Principal) -> Result<(), AuthError> {
        self.require_enabled_tenant(&principal.tenant_id)?;
        self.principals.insert(principal.id.clone(), principal);
        Ok(())
    }

    pub fn create_local_user(&mut self, principal_id: impl Into<String>) {
        self.create_local_user_in_tenant(principal_id, DEFAULT_TENANT_ID)
            .expect("default tenant is always enabled");
    }

    pub fn create_local_user_in_tenant(
        &mut self,
        principal_id: impl Into<String>,
        tenant_id: impl Into<String>,
    ) -> Result<(), AuthError> {
        self.upsert_principal(Principal::local_user_in_tenant(principal_id, tenant_id))
    }

    pub fn create_service_account(&mut self, principal_id: impl Into<String>) {
        self.create_service_account_in_tenant(principal_id, DEFAULT_TENANT_ID)
            .expect("default tenant is always enabled");
    }

    pub fn create_service_account_in_tenant(
        &mut self,
        principal_id: impl Into<String>,
        tenant_id: impl Into<String>,
    ) -> Result<(), AuthError> {
        self.upsert_principal(Principal::service_account_in_tenant(
            principal_id,
            tenant_id,
        ))
    }

    pub fn create_custom_identity(
        &mut self,
        principal_id: impl Into<String>,
        shared_secret: impl Into<String>,
    ) -> Result<(), AuthError> {
        let principal_id = principal_id.into();
        self.upsert_principal(Principal::custom_identity(principal_id.clone()))?;
        self.put_custom_identity(CustomIdentity::active(principal_id, shared_secret))
    }

    pub fn principal_tenant_id(&self, principal_id: &str) -> Option<&str> {
        self.principals
            .get(principal_id)
            .map(|principal| principal.tenant_id.as_str())
    }

    pub fn assign_operator_role(
        &mut self,
        principal_id: impl Into<String>,
        role: OperatorRole,
        scope: impl Into<String>,
    ) -> Result<RoleAssignment, AuthError> {
        let principal_id = principal_id.into();
        self.require_enabled_principal(&principal_id)?;
        let assignment = RoleAssignment::new(principal_id.clone(), role, scope);
        let assignments = self.role_assignments.entry(principal_id).or_default();
        if !assignments.contains(&assignment) {
            assignments.push(assignment.clone());
        }
        Ok(assignment)
    }

    pub fn role_assignments(&self, principal_id: &str) -> Vec<RoleAssignment> {
        self.role_assignments
            .get(principal_id)
            .cloned()
            .unwrap_or_default()
    }

    pub fn operator_action_allowed(
        &self,
        principal_id: &str,
        action: OperatorAction,
        resource: &str,
    ) -> Result<bool, AuthError> {
        self.require_enabled_principal(principal_id)?;
        Ok(self
            .role_assignments(principal_id)
            .iter()
            .any(|assignment| assignment.permits(action, resource)))
    }

    pub fn upsert_identity_provider(&mut self, provider: IdentityProvider) {
        self.identity_providers
            .insert(provider.id().to_string(), provider);
    }

    pub fn identity_provider(&self, provider_id: &str) -> Option<&IdentityProvider> {
        self.identity_providers.get(provider_id)
    }

    pub fn identity_provider_count(&self) -> usize {
        self.identity_providers.len()
    }

    pub fn put_custom_identity(&mut self, identity: CustomIdentity) -> Result<(), AuthError> {
        self.require_enabled_principal(&identity.principal_id)?;
        self.custom_identities
            .insert(identity.principal_id.clone(), identity);
        Ok(())
    }

    pub fn disable_custom_identity(&mut self, principal_id: &str) -> Result<(), AuthError> {
        let identity = self
            .custom_identities
            .get_mut(principal_id)
            .ok_or_else(|| AuthError::UnknownCustomIdentity(principal_id.to_string()))?;
        identity.disable();
        Ok(())
    }

    pub fn principal(&self, principal_id: &str) -> Option<&Principal> {
        self.principals.get(principal_id)
    }
}
