//! Exact interface lookup and Linker installation for host capabilities.

use wasmtime::component::Linker;

use super::{clock, kv};
use crate::{RuntimeError, runtime::HostState};

/// Stable description of one versioned WIT interface used by a Component.
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct CapabilityDescriptor {
    interface: &'static str,
    package: &'static str,
    name: &'static str,
    version: &'static str,
}

impl CapabilityDescriptor {
    const fn new(
        interface: &'static str,
        package: &'static str,
        name: &'static str,
        version: &'static str,
    ) -> Self {
        Self {
            interface,
            package,
            name,
            version,
        }
    }

    /// Returns the fully qualified and versioned WIT interface identity.
    pub const fn interface(&self) -> &'static str {
        self.interface
    }

    /// Returns the versioned WIT package identity without the version suffix.
    pub const fn package(&self) -> &'static str {
        self.package
    }

    /// Returns the WIT interface name inside the package.
    pub const fn name(&self) -> &'static str {
        self.name
    }

    /// Returns the exact semantic version implemented by the Runtime.
    pub const fn version(&self) -> &'static str {
        self.version
    }
}

#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub(crate) enum Capability {
    MonotonicClock,
    KvStore,
}

impl Capability {
    pub(crate) const fn descriptor(self) -> CapabilityDescriptor {
        match self {
            Self::MonotonicClock => CapabilityDescriptor::new(
                clock::INTERFACE,
                clock::PACKAGE,
                clock::NAME,
                clock::VERSION,
            ),
            Self::KvStore => {
                CapabilityDescriptor::new(kv::INTERFACE, kv::PACKAGE, kv::NAME, kv::VERSION)
            }
        }
    }

    pub(crate) fn add_to_linker(self, linker: &mut Linker<HostState>) -> Result<(), RuntimeError> {
        match self {
            Self::MonotonicClock => clock::add_to_linker(linker),
            Self::KvStore => kv::add_to_linker(linker),
        }
    }
}

/// Registry of the exact WIT interface versions implemented by this Runtime.
pub(crate) struct CapabilityRegistry;

impl CapabilityRegistry {
    /// Resolves an exact Component import to a supported host capability.
    pub(crate) fn resolve(interface: &str) -> Option<Capability> {
        match interface {
            clock::INTERFACE => Some(Capability::MonotonicClock),
            kv::INTERFACE => Some(Capability::KvStore),
            _ => None,
        }
    }

    /// Installs only the capabilities requested by one Component.
    pub(crate) fn add_to_linker(
        linker: &mut Linker<HostState>,
        capabilities: &[Capability],
    ) -> Result<(), RuntimeError> {
        for capability in capabilities {
            capability.add_to_linker(linker)?;
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::{Capability, CapabilityRegistry};

    #[test]
    fn resolves_only_the_exact_supported_interface_version() {
        assert_eq!(
            CapabilityRegistry::resolve("wasmeld:clock/monotonic-clock@0.1.0"),
            Some(Capability::MonotonicClock)
        );
        assert_eq!(
            CapabilityRegistry::resolve("wasmeld:clock/monotonic-clock@0.2.0"),
            None
        );
        assert_eq!(
            CapabilityRegistry::resolve("wasmeld:clock/other@0.1.0"),
            None
        );
        assert_eq!(
            CapabilityRegistry::resolve("wasmeld:kv/store@0.1.0"),
            Some(Capability::KvStore)
        );
        assert_eq!(CapabilityRegistry::resolve("wasmeld:kv/store@0.2.0"), None);
    }
}
