mod diff;
mod fs;
mod misc;

use clap::{Parser, Subcommand};
use humantime::{Duration, Timestamp};
use std::path::PathBuf;
use std::process::exit;

#[derive(Parser)] // requires `derive` feature
#[command(version, about, long_about = None)]
struct Cli {
    #[command(subcommand)]
    command: Commands,
}

#[derive(Subcommand)]
enum Commands {
    /// Initialize a repository with data
    #[command(arg_required_else_help = true)]
    Init {
        /// Directory of repoisitory to create
        repo: PathBuf,

        /// Directory of data to use
        data: PathBuf,

        /// Optional ignore file
        ignore: Option<PathBuf>,
    },

    /// Update a repository with new data
    #[command(arg_required_else_help = true)]
    Update {
        /// Directory of repository to update
        repo: PathBuf,

        /// Directory of data to add
        data: PathBuf,

        /// How old should revisions be kept
        keep: Duration,

        /// Print modifications without updating repository
        #[clap(long, short, action)]
        dry_run: bool,
    },

    /// Pull historical repository data
    #[command(arg_required_else_help = true)]
    Pull {
        /// Directory of repository to pull from
        repo: PathBuf,

        /// Directory to output data to
        out: PathBuf,

        /// Timestamp of revision to pull
        timestamp: Timestamp,
    },

    /// Revert updates to a repository
    #[command(arg_required_else_help = true)]
    Revert {
        /// Directory of repository to revert
        repo: PathBuf,

        /// Timestamp of revision to revert to
        timestamp: Timestamp,
    },

    /// Manually remove old updates
    #[command(arg_required_else_help = true)]
    Clean {
        /// Directory of repository to clean
        repo: PathBuf,

        /// How old should revisions be kept
        keep: Duration,
    },
}

fn main() {
    let cli = Cli::parse();
    let mut repo_dir = PathBuf::new();

    let err = match &cli.command {
        Commands::Init { repo, data, ignore } => {
            repo_dir = repo.clone();
            fs::init_repo(repo, data, ignore)
        }
        Commands::Update {
            repo,
            data,
            keep,
            dry_run: dry,
        } => {
            repo_dir = repo.clone();
            fs::create_patch(repo, data, *keep, *dry)
        }
        Commands::Pull {
            repo,
            out,
            timestamp,
        } => {
            repo_dir = repo.clone();
            fs::pull_patch(repo, out, timestamp.clone(), false)
        }
        Commands::Revert { repo, timestamp } => {
            repo_dir = repo.clone();
            fs::revert(repo, timestamp.clone())
        }
        Commands::Clean { repo, keep } => {
            repo_dir = repo.clone();
            fs::clean_repo(repo, *keep, None)
        }
    };

    fs::del_lock(&repo_dir).unwrap();
    if err.is_err() {
        println!("{:?}", err);
        exit(1);
    }
}
