use std::fs::File;
use std::path::Path;

use serde::{Deserialize, Serialize};

use crate::error::OLMResult;

const META_FILE: &str = "meta.json";

// ── IndexMode ─────────────────────────────────────────────────────────────────

/// Evidence mode for an entire partitioned index — homogeneous across all layers.
///
/// Determined once at build time; stored in `PartitionMeta` (`meta.json`).
/// All layers within an index share the same mode.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum IndexMode {
    /// Exact evidence: `evidence.bin` + `unitigs.bin.idx`.  Zero false positives.
    Exact,
    /// Approximate evidence: `fingerprint.bin` only.
    /// `b` — fingerprint bits per slot; false-positive rate ≈ 1/2^b per query.
    /// `z` — Findere consecutive-kmer parameter (build-time only; not used at query time).
    Approx { b: u8, z: u8 },
    /// Hybrid: both `fingerprint.bin` and `evidence.bin` + `unitigs.bin.idx`.
    /// `find()` uses the fingerprint (O(1), approx); `find_strict()` uses exact evidence.
    Hybrid { b: u8, z: u8 },
}

impl Default for IndexMode {
    fn default() -> Self { Self::Exact }
}

// ── PartitionMeta ─────────────────────────────────────────────────────────────

/// Index-level metadata stored in `meta.json` at the root of a partition index.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PartitionMeta {
    pub n_layers: usize,
    #[serde(default)]
    pub mode: IndexMode,
}

impl PartitionMeta {
    pub fn new(mode: IndexMode) -> Self {
        Self { n_layers: 0, mode }
    }

    pub fn load(dir: &Path) -> OLMResult<Self> {
        let f = File::open(dir.join(META_FILE))?;
        Ok(serde_json::from_reader(f)?)
    }

    pub fn save(&self, dir: &Path) -> OLMResult<()> {
        let f = File::create(dir.join(META_FILE))?;
        serde_json::to_writer_pretty(f, self)?;
        Ok(())
    }
}

impl Default for PartitionMeta {
    fn default() -> Self { Self::new(IndexMode::Exact) }
}
