use crate::cli::SyncArgs;
use crate::commands::core::{Command, CommandExecutor};
use crate::commands::git::run_cmd_passthrough;
use crate::utils::display::yellow;
use crate::utils::paths::get_mntn_dir;
use crate::utils::system::run_cmd;
use anyhow::{Context, Result, bail};
use chrono::Utc;
use std::path::Path;
use std::process::Command as ProcessCommand;

struct SyncTask {
    message: Option<String>,
}

impl SyncTask {
    fn new(message: Option<String>) -> Self {
        Self { message }
    }

    fn commit_message(&self) -> Result<String> {
        match self
            .message
            .as_ref()
            .map(|msg| msg.trim())
            .filter(|msg| !msg.is_empty())
        {
            Some(msg) => Ok(msg.to_string()),
            None => {
                let stamp = Utc::now().format("%Y-%m-%d %H:%M:%S UTC");
                Ok(format!("chore: sync mntn ({stamp})"))
            }
        }
    }

    fn has_staged_changes(repo: &Path) -> Result<bool> {
        let status = ProcessCommand::new("git")
            .args(["diff", "--cached", "--quiet"])
            .current_dir(repo)
            .status()
            .context("Checking staged changes")?;

        match status.code() {
            Some(0) => Ok(false),
            Some(1) => Ok(true),
            Some(code) => bail!("git diff --cached --quiet exited with status {}", code),
            None => bail!("git diff --cached --quiet was terminated by signal"),
        }
    }
}

impl Command for SyncTask {
    fn name(&self) -> &str {
        "Sync"
    }

    fn execute(&mut self) -> anyhow::Result<()> {
        let repo_dir = get_mntn_dir();
        crate::commands::git::ensure_git_repo(&repo_dir)?;

        run_cmd("git", &["add", "."], Some(&repo_dir))?;
        let staged = Self::has_staged_changes(&repo_dir)?;
        if staged {
            let message = self.commit_message()?;
            run_cmd("git", &["commit", "-m", &message], Some(&repo_dir))?;
        } else {
            println!("{}", yellow("   No changes to commit"));
        }

        run_cmd_passthrough("git", &["push"], Some(&repo_dir))?;
        Ok(())
    }
}

pub(crate) fn run(args: SyncArgs) {
    let mut task = SyncTask::new(args.message);
    CommandExecutor::run(&mut task);
}
