use std::io::Write;
use std::path::PathBuf;

use clap::Clap;
use libpijul::changestore::*;
use libpijul::{Base32, TxnT, TxnTExt};

use crate::repository::Repository;
use crate::Error;

#[derive(Clap, Debug)]
pub struct Log {
    /// Set the repository where this command should run. Defaults to the first ancestor of the current directory that contains a `.pijul` directory.
    #[clap(long = "repository")]
    repo_path: Option<PathBuf>,
    /// Show logs for this channel instead of the current channel
    #[clap(long = "channel")]
    channel: Option<String>,
    /// Only show the change hashes
    #[clap(long = "hash-only")]
    hash_only: bool,
    /// Include state identifiers in the output
    #[clap(long = "state")]
    states: bool,
    /// Include full change description in the output
    #[clap(long = "description")]
    descriptions: bool,
}

impl Log {
    pub fn run(self) -> Result<(), anyhow::Error> {
        let repo = Repository::find_root(self.repo_path)?;
        let txn = repo.pristine.txn_begin()?;
        let channel_name = repo.config.get_current_channel(self.channel.as_ref());
        let channel = if let Some(channel) = txn.load_channel(channel_name) {
            channel
        } else {
            return Err((Error::NoSuchChannel {
                channel: channel_name.to_string(),
            })
            .into());
        };
        super::pager();
        let changes = repo.changes;
        let mut stdout = std::io::stdout();
        if self.hash_only {
            for (_, (h, _)) in txn.reverse_log(&channel.borrow(), None) {
                writeln!(stdout, "{}", h.to_base32())?
            }
        } else {
            let states = self.states;
            for (_, (h, mrk)) in txn.reverse_log(&channel.borrow(), None) {
                let header = changes.get_header(&h)?;
                writeln!(stdout, "Change {}", h.to_base32())?;
                writeln!(stdout, "Author: {:?}", header.authors)?;
                writeln!(stdout, "Date: {}", header.timestamp)?;
                if states {
                    writeln!(stdout, "State: {}", mrk.to_base32())?;
                }
                writeln!(stdout, "\n    {}\n", header.message)?;
                if self.descriptions {
                    if let Some(ref descr) = header.description {
                        writeln!(stdout, "\n    {}\n", descr)?;
                    }
                }
            }
        }
        Ok(())
    }
}
