#[derive(Clone)]
struct ToolRegistryEntry {
    manifest: ToolManifest,
    binding: ToolBinding,
    /// Tool Catalog membership. A member is callable; a non-member does not
    /// exist to the model. Orphaned entries are never members.
    member: bool,
}

impl ToolRegistryEntry {
    fn new(manifest: ToolManifest, source_id: impl Into<String>) -> Self {
        Self {
            manifest,
            binding: ToolBinding::Bound(source_id.into()),
            member: true,
        }
    }

    fn orphaned(manifest: ToolManifest) -> Self {
        Self {
            manifest,
            binding: ToolBinding::Orphaned,
            member: true,
        }
    }

    fn is_orphaned(&self) -> bool {
        self.binding == ToolBinding::Orphaned
    }

    fn is_member(&self) -> bool {
        self.member && !self.is_orphaned()
    }

    /// The manifest as exposed to surfaces and catalogs. Membership is the
    /// execution gate, so the view is just the stored manifest; orphaned and
    /// host-removed entries are filtered out by the caller, not flagged here.
    fn view_manifest(&self) -> ToolManifest {
        self.manifest.clone()
    }

    fn export(&self) -> ToolStateEntry {
        ToolStateEntry {
            manifest: self.manifest.clone(),
            orphaned: self.is_orphaned(),
            member: self.member,
        }
    }
}

#[derive(Clone)]
struct ToolRegistryState {
    generation: u64,
    tools: BTreeMap<ToolId, ToolRegistryEntry>,
    next_live_source_id: u64,
}

/// Outcome of [`ToolRegistry::restore_state`]: the adopted generation plus the
/// ids of persisted tools that no registered source currently resolves.
/// Hosts should surface a non-empty `orphaned` list to the user — the session
/// opened, but those tools are non-members until their source returns.
#[derive(Clone, Debug, Default)]
pub struct ToolRestoreReport {
    pub generation: u64,
    pub orphaned: Vec<ToolId>,
}

#[derive(Debug, thiserror::Error)]
pub enum ReconfigureError {
    #[error("validation error: {0}")]
    Validation(String),
    #[error("unknown tool source: {0}")]
    UnknownSource(String),
    #[error("generation mismatch: expected {expected}, actual {actual}")]
    GenerationMismatch { expected: u64, actual: u64 },
}

#[derive(Clone)]
pub struct ToolRegistry {
    sources: Arc<RwLock<BTreeMap<String, Arc<dyn ToolSourceExecutor>>>>,
    state: Arc<RwLock<ToolRegistryState>>,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum SourceReconcilePolicy {
    RejectExternalConflicts,
    OverlayReplacingConflicts,
}
