// Copyright 2026 PingCAP, Inc.

use std::{
    path::PathBuf,
    sync::{Arc, Mutex},
};

use clap::Args;
use native_br::packing::{self, PackBackupStep, PackConfig, ReportPackBackupStepTrait};
use tikv_util::{info, time::Instant};

#[derive(Args)]
/// Arguments for the pack backup command
pub struct PackBackupArgs {
    /// The path of the config file
    #[clap(long, default_value = "")]
    pub config: PathBuf,

    /// The name of the backup to pack
    #[clap(long)]
    pub backup_name: String,

    /// The name of the keyspace
    #[clap(long)]
    pub keyspace_name: String,

    /// Confirm packing with the offline PD metadata stored in the backup.
    #[clap(
        long,
        action = clap::ArgAction::Set,
        default_value_t = true,
        min_values = 0,
        max_values = 1,
        default_missing_value = "true"
    )]
    pub offline: bool,

    /// Path of file that contains list of trusted SSL CAs
    #[clap(long, default_value = "")]
    pub cacert: PathBuf,

    /// Path of file that contains X509 certificate in PEM format
    #[clap(long, default_value = "")]
    pub cert: PathBuf,

    /// Path of file that contains X509 key in PEM format
    #[clap(long, default_value = "")]
    pub key: PathBuf,

    /// The directory to store temporary data
    #[clap(long)]
    pub data_dir: Option<PathBuf>,
}

pub struct CliReportPackBackupStep {
    last_step_start_time: Mutex<Instant>,
}

impl ReportPackBackupStepTrait for CliReportPackBackupStep {
    fn report_step(&self, step: PackBackupStep) {
        let mut last_step_time = self.last_step_start_time.lock().unwrap();
        let elapsed = last_step_time.saturating_elapsed();
        *last_step_time = Instant::now();
        info!("Starting a new step."; "step" => ?step, "last_step_takes" => ?elapsed);
    }
}

impl Default for CliReportPackBackupStep {
    fn default() -> Self {
        Self {
            last_step_start_time: Mutex::new(Instant::now()),
        }
    }
}

impl Drop for CliReportPackBackupStep {
    fn drop(&mut self) {
        let last_step_time = self.last_step_start_time.get_mut().unwrap();
        let elapsed = last_step_time.saturating_elapsed();
        info!("Finishing pack backup."; "takes" => ?elapsed);
    }
}

pub fn execute_pack_backup(args: PackBackupArgs) {
    if !args.offline {
        panic!("`--offline` must be true because online packing is no longer supported");
    }

    let mut config = PackConfig::default();
    if args.config.exists() {
        let data = std::fs::read(args.config.clone()).expect("failed to read config file");
        config = toml::from_slice(&data).unwrap();
    }

    // override from args and ENV
    config.backup_name = args.backup_name.clone();
    config.keyspace_name = args.keyspace_name;
    config.data_dir = args.data_dir;

    if args.cacert.exists() {
        config.security.ca_path = args.cacert.to_str().unwrap().to_owned();
    }
    if args.cert.exists() {
        config.security.cert_path = args.cert.to_str().unwrap().to_owned();
    }
    if args.key.exists() {
        config.security.key_path = args.key.to_str().unwrap().to_owned();
    }

    config.dfs.override_from_env();
    config.security.override_from_env();

    info!("Begin packing backup with config {:?}", config);

    let reporter = Arc::new(CliReportPackBackupStep::default());
    match packing::pack_backup_with_cfg(reporter, config) {
        Ok(meta) => {
            info!("Successfully packed backup: {}", args.backup_name);
            info!("Copyable path: {}", meta.copyable_path);
        }
        Err(e) => {
            panic!("Failed to pack backup: {:?}", e);
        }
    }
}
