use crate::client::KeepClient;
use crate::compression_engine::CompressionType;
use crate::filter_plugin::FilterChain;
use crate::modes::common::{check_binary_tty, resolve_item_id};
use crate::services::compression_service::CompressionService;
use anyhow::Result;
use clap::Command;
use log::debug;
use std::io::{Read, Write};
use std::str::FromStr;

pub fn mode(
    client: &KeepClient,
    cmd: &mut Command,
    settings: &crate::config::Settings,
    ids: &[i64],
    tags: &[String],
    filter_chain: Option<FilterChain>,
) -> Result<(), anyhow::Error> {
    debug!("CLIENT_GET: Getting item via remote server");

    if !ids.is_empty() && !tags.is_empty() {
        cmd.error(
            clap::error::ErrorKind::InvalidValue,
            "Both ID and tags given, you must supply either IDs or tags when using --get",
        )
        .exit();
    }

    let item_id = resolve_item_id(client, ids, tags)?;

    // Get item info for metadata
    let item_info = client.get_item_info(item_id)?;
    let metadata = &item_info.metadata;

    // Get streaming reader for raw content
    let (reader, compression) = client.get_item_content_stream(item_id)?;
    let compression_type = CompressionType::from_str(&compression).unwrap_or(CompressionType::None);

    // Decompress through streaming readers
    let mut decompressed_reader: Box<dyn Read> =
        CompressionService::decompressing_reader(reader, &compression_type);

    // Binary detection: sample first chunk
    let mut sample_buf = [0u8; crate::common::PIPESIZE];
    let sample_len = decompressed_reader.read(&mut sample_buf)?;
    check_binary_tty(metadata, &sample_buf[..sample_len], settings.force)?;

    // If filters present, buffer through filter chain; otherwise stream directly
    if let Some(mut chain) = filter_chain {
        // Apply filter to sample first, then remaining
        let mut output = Vec::new();
        chain.filter(&mut &sample_buf[..sample_len], &mut output)?;
        crate::common::stream_copy(&mut decompressed_reader, |chunk| {
            chain.filter(&mut std::io::Cursor::new(chunk), &mut output)?;
            Ok(())
        })?;
        let stdout = std::io::stdout();
        let mut stdout = stdout.lock();
        stdout.write_all(&output)?;
        stdout.flush()?;
    } else {
        // Stream decompressed content to stdout
        let stdout = std::io::stdout();
        let mut stdout = stdout.lock();
        stdout.write_all(&sample_buf[..sample_len])?;
        crate::common::stream_copy(&mut decompressed_reader, |chunk| {
            stdout.write_all(chunk)?;
            Ok(())
        })?;
        stdout.flush()?;
    }

    Ok(())
}
