use crate::inspect::cargo::NAMED_CARGO_PRICE_DESCRIPTOR_ROW_COUNT;

use super::bindings::CargoBindingRow;
use super::types::{CargoLiveRegistryEntry, CargoSelectorEntry, CargoSelectorReport};

pub(super) fn build_selector_from_bindings(
    bindings: &[CargoBindingRow],
    live_registry_entries: &[CargoLiveRegistryEntry],
    band: &str,
    selector_kind: &str,
    selector_row_count: usize,
    grounding_note: &str,
    order_note: &str,
) -> Option<CargoSelectorReport> {
    let mut rows = bindings
        .iter()
        .filter(|binding| binding.band == band)
        .collect::<Vec<_>>();
    rows.sort_by_key(|binding| binding.binding_index);
    if rows.is_empty() {
        return None;
    }

    let entries = rows
        .into_iter()
        .map(|binding| CargoSelectorEntry {
            selector_index: binding.binding_index,
            descriptor_id: Some(binding.descriptor_id),
            visible_name: binding.cargo_name.clone(),
            source_kinds: live_registry_entries
                .iter()
                .find(|entry| entry.visible_name == binding.cargo_name)
                .map(|entry| entry.source_kinds.clone())
                .unwrap_or_default(),
        })
        .collect::<Vec<_>>();

    Some(CargoSelectorReport {
        selector_kind: selector_kind.to_string(),
        exact_resolution: entries.len() == selector_row_count,
        selector_row_count,
        candidate_registry_count: live_registry_entries.len(),
        notes: vec![grounding_note.to_string(), order_note.to_string()],
        entries,
    })
}

pub(super) fn build_price_selector_candidate_registry(
    live_registry_entries: &[CargoLiveRegistryEntry],
) -> CargoSelectorReport {
    let entries = live_registry_entries
        .iter()
        .enumerate()
        .map(|(index, entry)| CargoSelectorEntry {
            selector_index: index + 1,
            descriptor_id: None,
            visible_name: entry.visible_name.clone(),
            source_kinds: entry.source_kinds.clone(),
        })
        .collect::<Vec<_>>();
    let candidate_registry_count = entries.len();
    let exact_resolution = candidate_registry_count == NAMED_CARGO_PRICE_DESCRIPTOR_ROW_COUNT;
    let mut notes = Vec::new();
    notes.push(
        "This is the current merged visible-name registry, sorted lexicographically, not a claimed reproduction of the original price selector."
            .to_string(),
    );
    if exact_resolution {
        notes.push(
            "The merged visible-name registry cardinality matches the 71-row named cargo-price descriptor strip."
                .to_string(),
        );
    } else {
        notes.push(format!(
            "The merged visible-name registry has {} entries, so the exact 71-row price-selector binding remains unresolved by static source recovery alone.",
            candidate_registry_count
        ));
        let excess =
            candidate_registry_count.saturating_sub(NAMED_CARGO_PRICE_DESCRIPTOR_ROW_COUNT);
        notes.push(format!(
            "Current unresolved gap is {} excess candidate names relative to the descriptor strip.",
            excess
        ));
    }

    CargoSelectorReport {
        selector_kind: "named_cargo_price_candidate_registry".to_string(),
        exact_resolution,
        selector_row_count: NAMED_CARGO_PRICE_DESCRIPTOR_ROW_COUNT,
        candidate_registry_count,
        notes,
        entries,
    }
}
