def load(p):
    raw = open(p, encoding='utf-8', newline='').read()
    return raw.replace('\r\n', '\n'), '\r\n' in raw

def save(p, s, crlf):
    open(p, 'w', encoding='utf-8', newline='').write(s.replace('\n', '\r\n') if crlf else s)

def once(s, old, new):
    assert s.count(old) == 1, (old[:80], s.count(old))
    return s.replace(old, new)

# ---- model: the versions, the pure label fn, the model field ---------------
p = 'crates/spt/src/picker/model.rs'
s, c = load(p)
s = once(s, '''/// The shipped+local profiles known for one adapter (gathered in `data.rs`).''', '''/// `adapter[:profile]` followed by ` v<version>` when the version is known
/// (releases#259). An unknown version renders the address alone, never a
/// placeholder; an empty address stays empty.
// [impl->REQ-PICKER-ADAPTER-VERSION]
pub fn versioned(address: &str, version: Option<&str>) -> String {
    match version.filter(|v| !v.is_empty() && !address.is_empty()) {
        Some(v) => format!("{address} v{v}"),
        None => address.to_string(),
    }
}

/// The adapter versions the picker can name (releases#259): this node's
/// registry, and each peer's published roster keyed by (node key, adapter).
/// A remote endpoint's adapter is versioned from ITS node's roster, never from
/// this node's install, which may differ.
// [impl->REQ-PICKER-ADAPTER-VERSION]
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct AdapterVersions {
    pub local: std::collections::BTreeMap<String, String>,
    pub peers: std::collections::BTreeMap<(String, String), String>,
}

impl AdapterVersions {
    /// The adapter name inside an `adapter[:profile]` address.
    fn adapter_of(address: &str) -> &str {
        address.split(':').next().unwrap_or(address)
    }

    /// This node's version of the adapter an address names.
    pub fn local_version(&self, address: &str) -> Option<&str> {
        self.local.get(Self::adapter_of(address)).map(String::as_str)
    }

    /// The version of `ep`'s adapter on the node the endpoint lives on.
    pub fn endpoint_version(&self, ep: &EndpointRow) -> Option<&str> {
        let name = Self::adapter_of(&ep.adapter_profile);
        if ep.is_local {
            self.local.get(name).map(String::as_str)
        } else {
            self.peers
                .get(&(ep.node_key.clone(), name.to_string()))
                .map(String::as_str)
        }
    }

    /// `ep`'s adapter address, versioned.
    pub fn endpoint_label(&self, ep: &EndpointRow) -> String {
        versioned(&ep.adapter_profile, self.endpoint_version(ep))
    }
}

/// The shipped+local profiles known for one adapter (gathered in `data.rs`).''')
s = once(s, '''    /// A transient one-line status (e.g. the shortcut-written confirmation),
    /// rendered in the bottom row until the next action clears it.
    pub flash: Option<String>,
}''', '''    /// A transient one-line status (e.g. the shortcut-written confirmation),
    /// rendered in the bottom row until the next action clears it.
    pub flash: Option<String>,

    /// Adapter versions for every place an adapter is named (releases#259).
    /// Stamped by `run()` like `run_cwd`; empty in tests that do not set it.
    // [impl->REQ-PICKER-ADAPTER-VERSION]
    pub adapter_versions: AdapterVersions,
}''')
s = once(s, '''            home_cursor: 0,
            flash: None,
        }''', '''            home_cursor: 0,
            flash: None,
            adapter_versions: AdapterVersions::default(),
        }''')
save(p, s, c)

# ---- data: gather both maps -------------------------------------------------
p = 'crates/spt/src/picker/data.rs'
s, c = load(p)
s = once(s, '''/// The shortcut basename an adapter option declares in its RESOLVED manifest''', '''/// Every adapter version the picker can name: this node's registered
/// adapters, and each peer's roster from the registry snapshots (no dial).
// [impl->REQ-PICKER-ADAPTER-VERSION]
pub fn adapter_versions() -> super::model::AdapterVersions {
    let adapters_dir = perch::adapters_dir();
    let local = spt_runtime::registry::registered(&adapters_dir)
        .into_iter()
        .map(|(record, manifest)| (record.name, manifest.adapter.version))
        .collect();
    let mut peers = std::collections::BTreeMap::new();
    for reg in crate::wansend::load_snapshots(&perch::identity_dir().join("registry")).values() {
        for (node, rows) in reg.node_adapters() {
            for row in rows {
                peers.insert((node.to_string(), row.name.clone()), row.version.clone());
            }
        }
    }
    super::model::AdapterVersions { local, peers }
}

/// The shortcut basename an adapter option declares in its RESOLVED manifest''')
save(p, s, c)

# ---- mod.rs: stamp it ---------------------------------------------------------
p = 'crates/spt/src/picker/mod.rs'
s, c = load(p)
s = once(s, '''    model.local_tab_label = data::own_node_tab_label();
''', '''    model.local_tab_label = data::own_node_tab_label();
    // [impl->REQ-PICKER-ADAPTER-VERSION]
    model.adapter_versions = data::adapter_versions();
''')
save(p, s, c)

# ---- view: every place an adapter is named ------------------------------------
p = 'crates/spt/src/picker/view.rs'
s, c = load(p)
s = once(s, '''        .map(|(i, row)| {
            let label = match &row.profile {
                Some(p) => format!("{} {}:{p}", glyph::BRANCH, row.adapter),
                None => row.adapter.clone(),
            };''', '''        .map(|(i, row)| {
            // [impl->REQ-PICKER-ADAPTER-VERSION]
            let version = model.adapter_versions.local_version(&row.adapter);
            let label = match &row.profile {
                Some(p) => format!(
                    "{} {}",
                    glyph::BRANCH,
                    super::model::versioned(&format!("{}:{p}", row.adapter), version)
                ),
                None => super::model::versioned(&row.adapter, version),
            };''')
s = once(s, '''    let adapter = model
        .selected_adapter()
        .map(|a| a.address())
        .unwrap_or_default();''', '''    // [impl->REQ-PICKER-ADAPTER-VERSION]
    let adapter = model
        .selected_adapter()
        .map(|a| {
            super::model::versioned(&a.address(), model.adapter_versions.local_version(&a.adapter))
        })
        .unwrap_or_default();''')
s = once(s, '''            Span::styled("harness: ", Style::default().fg(Color::DarkGray)),
            Span::raw(ep.adapter_profile.clone()),''', '''            Span::styled("harness: ", Style::default().fg(Color::DarkGray)),
            // [impl->REQ-PICKER-ADAPTER-VERSION]
            Span::raw(model.adapter_versions.endpoint_label(ep)),''')
s = once(s, '''            Line::from(ep.adapter_profile.clone()),
            Line::from(format!("Project history: {history}")),''', '''            // [impl->REQ-PICKER-ADAPTER-VERSION]
            Line::from(model.adapter_versions.endpoint_label(ep)),
            Line::from(format!("Project history: {history}")),''')
save(p, s, c)
print('ok')
