/* Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

use crate::IggyMessagesBatch;

/// Holds batches that are being written to disk.
///
/// During async I/O, messages are transferred from the journal to disk.
/// This buffer holds frozen (immutable, Arc-backed) copies so consumers
/// can still read them during the write operation.
#[derive(Debug, Default)]
pub struct IggyMessagesBatchSetInFlight {
    batches: Vec<IggyMessagesBatch>,
    first_offset: u64,
    last_offset: u64,
}

impl IggyMessagesBatchSetInFlight {
    pub fn is_empty(&self) -> bool {
        self.batches.is_empty()
    }

    pub fn first_offset(&self) -> u64 {
        self.first_offset
    }

    pub fn last_offset(&self) -> u64 {
        self.last_offset
    }

    pub fn set(&mut self, batches: Vec<IggyMessagesBatch>) {
        if batches.is_empty() {
            self.clear();
            return;
        }
        self.first_offset = batches.first().and_then(|b| b.first_offset()).unwrap_or(0);
        self.last_offset = batches.last().and_then(|b| b.last_offset()).unwrap_or(0);
        self.batches = batches;
    }

    pub fn clear(&mut self) {
        self.batches.clear();
        self.first_offset = 0;
        self.last_offset = 0;
    }

    /// Get batches that overlap with the requested offset range.
    /// Returns only the batches that contain messages within [start_offset, start_offset + count).
    pub fn get_by_offset(&self, start_offset: u64, count: u32) -> &[IggyMessagesBatch] {
        if self.is_empty() || start_offset > self.last_offset || count == 0 {
            return &[];
        }

        let end_offset = start_offset + count as u64 - 1;
        if end_offset < self.first_offset {
            return &[];
        }

        let start_idx = self
            .batches
            .iter()
            .position(|b| {
                b.last_offset()
                    .map(|last| last >= start_offset)
                    .unwrap_or(false)
            })
            .unwrap_or(0);

        let end_idx = self
            .batches
            .iter()
            .rposition(|b| {
                b.first_offset()
                    .map(|first| first <= end_offset)
                    .unwrap_or(false)
            })
            .map(|i| i + 1)
            .unwrap_or(self.batches.len());

        &self.batches[start_idx..end_idx]
    }

    pub fn batches(&self) -> &[IggyMessagesBatch] {
        &self.batches
    }
}
