use anyhow::Result;
use clap::Command;
use std::io::{Read, Write};

use crate::config;
use crate::services::item_service::ItemService;

/// Validates save mode arguments and exits with error if invalid.
///
/// This function checks that no item IDs are provided for save mode,
/// as save operations create new items rather than modifying existing ones.
///
/// # Arguments
///
/// * `cmd` - Mutable reference to the Clap command for error reporting.
/// * `ids` - Reference to the vector of item IDs (should be empty for save mode).
///
/// # Panics
///
/// Exits the program via Clap error if IDs are provided.
fn validate_save_args(cmd: &mut Command, ids: &[i64]) {
    if !ids.is_empty() {
        cmd.error(
            clap::error::ErrorKind::InvalidValue,
            "ID given, you cannot supply IDs when using --save",
        )
        .exit();
    }
}

/// A tee reader that duplicates input to both a reader and a writer as it reads.
///
/// This struct implements the `Read` trait and forwards all read operations to
/// an underlying reader while simultaneously writing the same data to a writer.
/// It's useful for saving content to a file while also echoing it to stdout.
///
/// # Fields
///
/// * `reader` - The underlying reader providing the data source.
/// * `writer` - The writer receiving copies of all read data.
struct TeeReader<R: Read, W: Write> {
    reader: R,
    writer: W,
}

impl<R: Read, W: Write> Read for TeeReader<R, W> {
    /// Reads data from the underlying reader and duplicates it to the writer.
    ///
    /// This implementation reads from the inner reader and then writes the same
    /// bytes to the writer. If the read returns 0 bytes (EOF), it returns 0.
    ///
    /// # Arguments
    ///
    /// * `buf` - Buffer to fill with data from the reader.
    ///
    /// # Returns
    ///
    /// * `io::Result<usize>` - Number of bytes read, or an I/O error.
    ///
    /// # Errors
    ///
    /// Returns an error if the underlying read or write operations fail.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// let mut tee = TeeReader {
    ///     reader: std::io::Cursor::new(b"Hello, world!"),
    ///     writer: std::io::sink(),
    /// };
    /// let mut buf = [0; 5];
    /// let n = tee.read(&mut buf).unwrap();
    /// assert_eq!(n, 5);
    /// assert_eq!(&buf[..n], b"Hello");
    /// ```
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        let n = self.reader.read(buf)?;
        if n > 0 {
            self.writer.write_all(&buf[..n])?;
        }
        Ok(n)
    }
}

/// Main save mode function.
///
/// This function handles the save operation by reading from stdin, duplicating
/// the input to stdout (for real-time display), and saving the content to the
/// item service. It validates arguments, creates the tee reader, and processes
/// the save operation.
///
/// # Arguments
///
/// * `cmd` - Mutable reference to the Clap command for error handling.
/// * `settings` - Application settings containing configuration.
/// * `ids` - Mutable vector of item IDs (should be empty for save mode).
/// * `tags` - Mutable vector of tags to associate with the new item.
/// * `conn` - Mutable reference to the database connection.
/// * `data_path` - Path to the data storage directory.
///
/// # Returns
///
/// * `Result<(), anyhow::Error>` - Success or error if save fails.
///
/// # Examples
///
/// ```ignore
/// // In CLI context, this would be called internally
/// mode_save(&mut cmd, &settings, &mut vec![], &mut vec!["important".to_string()], &mut conn, data_path)?;
/// ```
pub fn mode_save(
    cmd: &mut Command,
    settings: &config::Settings,
    ids: &mut [i64],
    tags: &mut Vec<String>,
    conn: &mut rusqlite::Connection,
    data_path: std::path::PathBuf,
) -> Result<(), anyhow::Error> {
    validate_save_args(cmd, ids);

    let item_service = ItemService::new(data_path);

    let stdin = std::io::stdin();
    let stdout = std::io::stdout();

    let tee_reader = TeeReader {
        reader: stdin.lock(),
        writer: stdout.lock(),
    };

    item_service.save_item(tee_reader, cmd, settings, tags, conn)?;

    Ok(())
}
