//! Streaming superkmer iterator over a [`NucPageCursor`].

use obiread::NucPageCursor;
use obikseq::RoutableSuperKmer;
use obikseq::kmer::Minimizer;

use crate::rolling_stat::RollingStat;
use crate::scratch::SuperKmerScratch;

/// Streaming iterator over [`RoutableSuperKmer`] values from a [`NucPageCursor`].
///
/// Cut conditions (checked in order per nucleotide, once the k-mer window is full):
///
/// | Condition              | cursor rewind |
/// |------------------------|---------------|
/// | entropy(kmer) ≤ θ      | k−1           |
/// | minimizer changed      | k             |
/// | super-kmer length = 256| k             |
pub struct SuperKmerStreamIter<'a> {
    cursor:       NucPageCursor<'a>,
    k:            usize,
    theta:        f64,
    scratch:      SuperKmerScratch,
    stat:         RollingStat,
    prev_min:     Option<Minimizer>,
    prev_min_pos: usize,
}

impl<'a> SuperKmerStreamIter<'a> {
    /// Build an iterator from a [`NucPageCursor`] over normalised sequence data.
    pub fn new(cursor: NucPageCursor<'a>, k: usize, level_max: usize, theta: f64) -> Self {
        Self {
            cursor,
            k,
            theta,
            scratch:      SuperKmerScratch::new(),
            stat:         RollingStat::new(level_max),
            prev_min:     None,
            prev_min_pos: 0,
        }
    }

    fn reset(&mut self) {
        self.stat.reset();
        self.scratch.reset();
        self.prev_min = None;
        self.prev_min_pos = 0;
    }

    fn try_emit(&mut self) -> Option<RoutableSuperKmer> {
        if self.scratch.len() < self.k {
            return None;
        }
        self.prev_min?;
        Some(self.scratch.emit(self.prev_min_pos))
    }
}

impl Iterator for SuperKmerStreamIter<'_> {
    type Item = RoutableSuperKmer;

    fn next(&mut self) -> Option<RoutableSuperKmer> {
        loop {
            let byte = match self.cursor.next_byte() {
                None => {
                    return self.try_emit();
                }
                Some(0x00) => {
                    let result = self.try_emit();
                    self.reset();
                    if result.is_some() {
                        return result;
                    }
                    continue;
                }
                Some(b) => b,
            };

            self.stat.push(byte);

            if !self.stat.ready() {
                self.scratch.push(byte);
                continue;
            }

            // ── 1. Entropy check ─────────────────────────────────────────────
            if self.stat.normalized_entropy().unwrap_or(1.0) < self.theta {
                let result = self.try_emit();
                self.cursor.rewind(self.k - 1);
                self.reset();
                if result.is_some() {
                    return result;
                }
                continue;
            }

            let min     = self.stat.canonical_minimizer().unwrap();
            let min_pos = self.stat.minimizer_position().unwrap_or(0);

            // ── 2. Minimizer change ───────────────────────────────────────────
            if let Some(prev) = self.prev_min {
                if min != prev {
                    let result = self.try_emit();
                    self.cursor.rewind(self.k);
                    self.reset();
                    if result.is_some() {
                        return result;
                    }
                    continue;
                }
            }

            // ── 3. Super-kmer length cap ──────────────────────────────────────
            if self.scratch.len() == 256 {
                let result = self.try_emit();
                self.cursor.rewind(self.k);
                self.reset();
                if result.is_some() {
                    return result;
                }
                continue;
            }

            self.prev_min     = Some(min);
            self.prev_min_pos = min_pos;
            self.scratch.push(byte);
        }
    }
}
