//! Super-kmer with routing metadata: canonical sequence + pre-computed minimizer.

use serde::Serialize;

use crate::Annotation;
use crate::Sequence;
use crate::SuperKmer;
use crate::kmer::Minimizer;
use crate::packed_seq::{PackedSeq, count_to_tail};
use crate::params::m;

/// Owned wrapper that pairs a canonical [`SuperKmer`] with its pre-computed minimizer.
///
/// Created at the single point where raw sequence bytes are emitted from the
/// scratch buffer. The minimizer position (given in original orientation) is
/// adjusted for any flip applied during canonicalisation. After routing, call
/// [`into_superkmer`] to discard the metadata and continue with the bare sequence.
///
/// [`into_superkmer`]: RoutableSuperKmer::into_superkmer
#[derive(Clone)]
pub struct RoutableSuperKmer {
    superkmer: SuperKmer,
    minimizer: Minimizer,
}

#[derive(Serialize)]
struct SKRAnnotation {
    seq_length: usize,
    count: u32,
}

impl Annotation for SKRAnnotation {}

impl RoutableSuperKmer {
    /// Construct from raw packed bytes.
    ///
    /// `min_pos` is the 0-based minimizer position in the **original** (pre-flip)
    /// orientation. `seql` is the nucleotide count. The sequence is canonicalised
    /// in place; `min_pos` is adjusted accordingly.
    pub fn build(min_pos: usize, seql: usize, seq: Box<[u8]>) -> Self {
        let mut inner = PackedSeq::new(count_to_tail(seql), seq);
        let already_canonical = inner.canonicalize();
        let adjusted_pos = if already_canonical {
            min_pos
        } else {
            seql - m() - min_pos
        };
        let minimizer = inner.mmer(adjusted_pos).unwrap();
        let superkmer = SuperKmer { count: 1, inner };
        Self { superkmer, minimizer }
    }

    /// Borrow the canonical super-kmer sequence.
    pub fn superkmer(&self) -> &SuperKmer {
        &self.superkmer
    }

    /// Borrow the canonical minimizer.
    pub fn minimizer(&self) -> &Minimizer {
        &self.minimizer
    }

    /// Consume this wrapper and return the inner [`SuperKmer`].
    pub fn into_superkmer(self) -> SuperKmer {
        self.superkmer
    }

    /// Sequence length in nucleotides.
    pub fn seql(&self) -> usize {
        self.superkmer.seql()
    }
}

impl PartialEq for RoutableSuperKmer {
    fn eq(&self, other: &Self) -> bool {
        self.superkmer == other.superkmer
    }
}

impl Eq for RoutableSuperKmer {}

impl std::hash::Hash for RoutableSuperKmer {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        self.superkmer.hash(state);
    }
}

impl Sequence for RoutableSuperKmer {
    type Canonical = RoutableSuperKmer;

    fn seql(&self) -> usize {
        self.superkmer.seql()
    }

    fn nucleotide(&self, i: usize) -> u8 {
        self.superkmer.nucleotide(i)
    }

    fn seq_hash(&self) -> u64 {
        self.minimizer.seq_hash()
    }

    fn canonical(&self) -> Self::Canonical {
        self.clone()
    }

    fn annotation(&self) -> impl Annotation {
        SKRAnnotation {
            seq_length: self.superkmer.seql(),
            count: self.superkmer.count(),
        }
    }
}
