//! Product loading state and asset types.

/// Tracks where the product is in its loading lifecycle.
///
/// The host transitions through these states as it fetches and verifies
/// the product bundle before handing off to the WebView.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ProductLoadState {
    /// No load has been initiated for this product.
    Idle,
    /// The host is resolving the .dot name or IPFS CID.
    Resolving,
    /// The product bundle is being fetched from IPFS / CDN.
    Fetching,
    /// The bundle has been fetched; integrity verification is in progress.
    Verifying,
    /// Assets are fully verified and available — the WebView may be loaded.
    Ready,
    /// The load failed; the inner string carries the human-readable reason.
    Failed(String),
}

/// The resolved and verified static assets for a product.
///
/// Obtained after `ProductLoadState::Ready` is reached. The host passes
/// these bytes to the WebView loader.
#[derive(Debug, Clone)]
pub struct ProductAssets {
    /// Raw bytes of the main HTML entry point (index.html or equivalent).
    pub index_html: Vec<u8>,
    /// MIME type reported by the bundle manifest for the index document.
    pub index_mime: String,
    /// Additional resources keyed by their bundle-relative path.
    ///
    /// The map covers all non-index assets (JS, CSS, WASM, fonts, …)
    /// so the host can serve them synchronously from memory, avoiding
    /// any network round-trips after the initial fetch.
    pub resources: std::collections::HashMap<String, Vec<u8>>,
}

impl ProductAssets {
    /// Construct a minimal assets bundle from just an HTML document.
    ///
    /// Useful for testing and for products that ship a single-file app.
    pub fn from_html(html: Vec<u8>) -> Self {
        Self {
            index_html: html,
            index_mime: "text/html; charset=utf-8".to_string(),
            resources: std::collections::HashMap::new(),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_from_html_sets_correct_mime() {
        let assets = ProductAssets::from_html(b"<h1>hi</h1>".to_vec());
        assert_eq!(assets.index_mime, "text/html; charset=utf-8");
        assert_eq!(assets.index_html, b"<h1>hi</h1>");
        assert!(assets.resources.is_empty());
    }

    #[test]
    fn test_load_state_equality() {
        assert_eq!(ProductLoadState::Idle, ProductLoadState::Idle);
        assert_ne!(ProductLoadState::Ready, ProductLoadState::Idle);
        assert_eq!(
            ProductLoadState::Failed("oops".into()),
            ProductLoadState::Failed("oops".into()),
        );
        assert_ne!(
            ProductLoadState::Failed("a".into()),
            ProductLoadState::Failed("b".into()),
        );
    }

    #[test]
    fn test_load_state_clone() {
        let s = ProductLoadState::Failed("timeout".into());
        let s2 = s.clone();
        assert_eq!(s, s2);
    }

    #[test]
    fn test_assets_clone() {
        let mut assets = ProductAssets::from_html(b"<p>x</p>".to_vec());
        assets
            .resources
            .insert("app.js".into(), b"console.log()".to_vec());
        let cloned = assets.clone();
        assert_eq!(cloned.index_html, assets.index_html);
        assert_eq!(cloned.resources.len(), 1);
    }
}
