use crate::api::HttpClient;
use crate::error::BioLibError;

use super::remote_endpoints::RemoteEndpoint;

pub trait IndexableBuffer {
    fn get_data(&self, start: u64, length: u64) -> crate::Result<Vec<u8>>;
}

pub struct RemoteIndexableBuffer {
    endpoint: Box<dyn RemoteEndpoint>,
    http: HttpClient,
}

impl RemoteIndexableBuffer {
    pub fn new(endpoint: Box<dyn RemoteEndpoint>) -> Self {
        Self {
            endpoint,
            http: HttpClient::shared(),
        }
    }
}

impl IndexableBuffer for RemoteIndexableBuffer {
    fn get_data(&self, start: u64, length: u64) -> crate::Result<Vec<u8>> {
        if length == 0 {
            return Ok(Vec::new());
        }

        let url = self.endpoint.get_remote_url()?;
        let end = start + length - 1;
        crate::logging::debug(&format!("Fetching bytes {start}-{end} ({length} bytes)"));
        let response = self.http.get_with_range(&url, start, end)?;

        if response.body.len() != length as usize {
            return Err(BioLibError::BinaryFormat(format!(
                "Expected {} bytes, got {}",
                length,
                response.body.len()
            )));
        }

        Ok(response.body)
    }
}

pub struct InMemoryIndexableBuffer {
    data: Vec<u8>,
}

impl InMemoryIndexableBuffer {
    pub fn new(data: Vec<u8>) -> Self {
        Self { data }
    }
}

impl IndexableBuffer for InMemoryIndexableBuffer {
    fn get_data(&self, start: u64, length: u64) -> crate::Result<Vec<u8>> {
        if length == 0 {
            return Ok(Vec::new());
        }

        let s = start as usize;
        let e = s + length as usize;
        if e > self.data.len() {
            return Err(BioLibError::BinaryFormat(
                "Read past end of buffer".to_string(),
            ));
        }
        Ok(self.data[s..e].to_vec())
    }
}
