use std::collections::{HashMap, HashSet};
use logisheets_base::{BlockCellId, BlockId, CellId, SheetId, TextId};
use crate::{block_manager::schema_manager::schema::BlockCellRole, errors::Error};
use super::status::Status;
fn key_text(status: &Status, sheet_id: SheetId, id: &BlockCellId) -> String {
let fetcher = |t: TextId| status.text_id_manager.get_string(&t).unwrap_or_default();
match status.container.get_cell(sheet_id, &CellId::BlockCell(*id)) {
Some(cell) => cell.value.to_string(&fetcher),
None => String::new(),
}
}
pub(crate) fn keys_by_value(
status: &Status,
sheet_id: SheetId,
block_id: BlockId,
) -> Option<HashMap<String, Vec<usize>>> {
let bp = status
.navigator
.get_block_place(&sheet_id, &block_id)
.ok()?;
let all_key_cells = status
.block_schema_manager
.get_all_key_cell_ids_by_block(sheet_id, block_id, bp)?;
let mut by_value: HashMap<String, Vec<usize>> = HashMap::new();
for (record_idx, id) in all_key_cells.iter().enumerate() {
let v = key_text(status, sheet_id, id);
if v.is_empty() {
continue;
}
by_value.entry(v).or_default().push(record_idx);
}
Some(by_value)
}
pub fn check_block_key_uniqueness(
status: &Status,
written: &HashSet<(SheetId, CellId)>,
) -> Result<(), Error> {
let mut touched: HashMap<(SheetId, BlockId), Vec<BlockCellId>> = HashMap::new();
for (sheet_id, cell_id) in written {
let CellId::BlockCell(bcid) = cell_id else {
continue;
};
if !matches!(
status.block_schema_manager.cell_role(*sheet_id, bcid),
BlockCellRole::Key
) {
continue;
}
touched
.entry((*sheet_id, bcid.block_id))
.or_default()
.push(*bcid);
}
if touched.is_empty() {
return Ok(());
}
let mut blocks: Vec<((SheetId, BlockId), Vec<BlockCellId>)> = touched.into_iter().collect();
blocks.sort_by_key(|((sheet_id, block_id), _)| (*sheet_id, *block_id));
for ((sheet_id, block_id), mut written_keys) in blocks {
let Some(by_value) = keys_by_value(status, sheet_id, block_id) else {
continue;
};
written_keys.sort_by_key(|id| (id.row, id.col));
for id in written_keys {
let v = key_text(status, sheet_id, &id);
if v.is_empty() {
continue;
}
if by_value.get(&v).map_or(0, |g| g.len()) > 1 {
let block = status
.block_schema_manager
.fetch_block_ref_name(sheet_id, block_id)
.unwrap_or_else(|| format!("#{}", block_id));
return Err(Error::DuplicateBlockKey { block, key: v });
}
}
}
Ok(())
}