use std::path::PathBuf;

use common::bitvec::DeletedBitVec;
use common::persisted_hashmap::{Key, UniversalHashMap};
use common::types::PointOffsetType;
use common::universal_io::{MmapFile, UniversalRead};
use serde::{Deserialize, Serialize};

use super::MapIndexKey;
use super::prefix_index::PrefixIndex;
use crate::index::field_index::on_disk_point_to_values::OnDiskPointToValues;

mod lifecycle;
mod live_reload;
mod read_ops;

pub(super) const DELETED_PATH: &str = "deleted.bin";
pub(super) const HASHMAP_PATH: &str = "values_to_points.bin";
pub(super) const CONFIG_PATH: &str = "mmap_field_index_config.json";

/// Immutable map index served directly from a [`UniversalRead`] storage backend.
///
/// On-disk state (`values_to_points.bin`, `deleted_mask.bin`,
/// `point_to_values.*`, `mmap_field_index_config.json`) is written once during
/// [`Self::build`] and not mutated afterwards: `deleted_mask.bin` (legacy
/// `deleted.bin` on older segments) records only the points whose payload was
/// empty at build time.
///
/// Runtime deletions live in the in-memory `Storage::deleted` bitvec. They are
/// **not persisted** — [`Self::flusher`] is a no-op and [`Self::remove_point`]
/// only updates the in-memory bitvec. Callers must re-supply the authoritative
/// deletion set (typically `id_tracker.deleted_point_bitslice()`) via the
/// `deleted_points` argument to [`Self::open`] on reload.
pub struct OnDiskMapIndex<N: MapIndexKey + Key + ?Sized, S: UniversalRead = MmapFile> {
    pub(super) path: PathBuf,
    pub(super) storage: Storage<N, S>,
    pub(super) total_key_value_pairs: usize,
    /// Whether the "no values" mask was read from the compact
    /// `deleted_mask.bin` or the legacy `deleted.bin`.
    pub(super) compact_deleted_mask: bool,
}

pub(super) struct Storage<N: MapIndexKey + Key + ?Sized, S: UniversalRead = MmapFile> {
    pub(super) value_to_points: UniversalHashMap<N, PointOffsetType, S>,
    pub(super) point_to_values: OnDiskPointToValues<N, S>,
    /// In-memory deletion bitmap. Reconstructed at load time as the union of
    /// the build-time empty-payload mask read from disk and the segment-level
    /// deleted bitslice supplied by the id-tracker. Not persisted.
    pub(super) deleted: DeletedBitVec,
    /// Sorted key dictionary for prefix queries. Present only when the index
    /// was built with the `prefix` option (signalled by the presence of its
    /// file on disk).
    pub(super) prefix_index: Option<PrefixIndex<S>>,
}

impl<N: MapIndexKey + Key + ?Sized, S: UniversalRead> Storage<N, S> {
    pub(super) fn ram_usage_bytes(&self) -> usize {
        let Self {
            value_to_points: _,
            point_to_values,
            deleted,
            prefix_index,
        } = self;

        // `value_to_points` is a storage-backed hashmap with no in-memory state.
        point_to_values.ram_usage_bytes()
            + deleted.ram_usage_bytes()
            + prefix_index
                .as_ref()
                .map_or(0, |prefix_index| prefix_index.ram_usage_bytes())
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub(super) struct UniversalMapIndexConfig {
    pub(super) total_key_value_pairs: usize,
}
