use anyhow::{Context, Result, anyhow};
use chrono::Utc;
use clap::Command;
use log::debug;
use std::collections::HashMap;
use std::fs;
use std::io::{Read, Write};

use crate::client::KeepClient;
use crate::config;
use crate::modes::common::{ExportMeta, resolve_item_id, sanitize_tags};

/// Export an item to data and metadata files via client.
///
/// If no IDs or tags are specified, exports the latest item.
/// Streams data in fixed-size buffers without loading entire file into memory.
pub fn mode(
    client: &KeepClient,
    cmd: &mut Command,
    settings: &config::Settings,
    ids: &[i64],
    tags: &[String],
) -> Result<()> {
    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 --export",
        )
        .exit();
    } else if ids.len() > 1 {
        cmd.error(
            clap::error::ErrorKind::InvalidValue,
            "More than one ID given, you must supply exactly one ID when using --export",
        )
        .exit();
    }

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

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

    // Get streaming reader for raw compressed content
    let (mut reader, compression) = client.get_item_content_stream(item_id)?;

    // Build template variables
    let mut vars = HashMap::new();
    vars.insert("id".to_string(), item_id.to_string());
    vars.insert("tags".to_string(), sanitize_tags(&item_info.tags));
    let ts = chrono::DateTime::parse_from_rfc3339(&item_info.ts)
        .map(|dt| dt.with_timezone(&Utc))
        .unwrap_or_else(|_| Utc::now());
    vars.insert(
        "ts".to_string(),
        ts.format("%Y-%m-%dT%H:%M:%SZ").to_string(),
    );
    vars.insert("compression".to_string(), compression.clone());

    let basename = strfmt::strfmt(&settings.export_filename_format, &vars).map_err(|e| {
        anyhow!(
            "Invalid export filename format '{}': {}",
            settings.export_filename_format,
            e
        )
    })?;

    // Stream data file write with fixed-size buffers
    let data_filename = format!("{}.data.{}", basename, compression);
    let mut data_file = fs::File::create(&data_filename)
        .with_context(|| format!("Cannot create data file: {}", data_filename))?;
    let mut total_bytes: usize = 0;
    crate::common::stream_copy(&mut reader, |chunk| {
        data_file.write_all(chunk)?;
        total_bytes += chunk.len();
        Ok(())
    })?;
    debug!(
        "CLIENT_EXPORT: Wrote {} bytes to {}",
        total_bytes, data_filename
    );

    // Write meta file
    let meta_filename = format!("{}.meta.yml", basename);
    let export_meta = ExportMeta {
        ts,
        compression,
        size: item_info.size,
        tags: item_info.tags.clone(),
        metadata: item_info.metadata.clone(),
    };
    let meta_yaml = serde_yaml::to_string(&export_meta)?;
    fs::write(&meta_filename, &meta_yaml)
        .with_context(|| format!("Cannot write meta file: {}", meta_filename))?;
    debug!("CLIENT_EXPORT: Wrote metadata to {}", meta_filename);

    eprintln!("{} {}", data_filename, meta_filename);

    Ok(())
}
