use assert_cmd::cargo::cargo_bin_cmd;
use predicates::prelude::*;
use std::fs;
use std::path::PathBuf;
use tempfile::TempDir;

fn init_quicommit(config_path: &PathBuf) {
    let mut cmd = cargo_bin_cmd!("quicommit");
    cmd.args(&["init", "--yes", "--config", config_path.to_str().unwrap()]);
    cmd.assert().success();
}

mod config_export {
    use super::*;

    #[test]
    fn test_export_to_stdout() {
        let temp_dir = TempDir::new().unwrap();
        let config_path = temp_dir.path().join("config.toml");
        init_quicommit(&config_path);

        let mut cmd = cargo_bin_cmd!("quicommit");
        cmd.args(&[
            "config",
            "export",
            "--config",
            config_path.to_str().unwrap(),
        ]);

        cmd.assert()
            .success()
            .stdout(predicate::str::contains("version"))
            .stdout(predicate::str::contains("[llm]"));
    }

    #[test]
    fn test_export_to_file() {
        let temp_dir = TempDir::new().unwrap();
        let config_path = temp_dir.path().join("config.toml");
        let export_path = temp_dir.path().join("exported.toml");
        init_quicommit(&config_path);

        let mut cmd = cargo_bin_cmd!("quicommit");
        cmd.args(&[
            "config",
            "export",
            "--config",
            config_path.to_str().unwrap(),
            "--output",
            export_path.to_str().unwrap(),
            "--password",
            "",
        ]);

        cmd.assert()
            .success()
            .stdout(predicate::str::contains("Configuration exported"));

        assert!(export_path.exists(), "Export file should be created");

        let content = fs::read_to_string(&export_path).unwrap();
        assert!(content.contains("version"), "Export should contain version");
        assert!(
            content.contains("[llm]"),
            "Export should contain LLM config"
        );
    }

    #[test]
    fn test_export_encrypted() {
        let temp_dir = TempDir::new().unwrap();
        let config_path = temp_dir.path().join("config.toml");
        let export_path = temp_dir.path().join("encrypted.toml");
        init_quicommit(&config_path);

        let mut cmd = cargo_bin_cmd!("quicommit");
        cmd.args(&[
            "config",
            "export",
            "--config",
            config_path.to_str().unwrap(),
            "--output",
            export_path.to_str().unwrap(),
            "--password",
            "test_password_123",
        ]);

        cmd.assert()
            .success()
            .stdout(predicate::str::contains("encrypted and exported"));

        assert!(export_path.exists(), "Export file should be created");

        let content = fs::read_to_string(&export_path).unwrap();
        assert!(
            content.starts_with("ENCRYPTED:"),
            "Encrypted file should start with ENCRYPTED:"
        );
        assert!(
            !content.contains("[llm]"),
            "Encrypted content should not be readable"
        );
    }
}

mod config_import {
    use super::*;

    #[test]
    fn test_import_plain_config() {
        let temp_dir = TempDir::new().unwrap();
        let config_path = temp_dir.path().join("config.toml");
        let import_path = temp_dir.path().join("import.toml");

        let plain_config = r#"
version = "1"

[llm]
provider = "openai"
model = "gpt-4"
max_tokens = 1000
temperature = 0.7
timeout = 60
api_key_storage = "keyring"

[commit]
format = "conventional"
auto_generate = true

[tag]
version_prefix = "v"
auto_generate = true

[changelog]
path = "CHANGELOG.md"
auto_generate = true

[language]
output_language = "en"
keep_types_english = true
keep_changelog_types_english = true
"#;
        fs::write(&import_path, plain_config).unwrap();

        let mut cmd = cargo_bin_cmd!("quicommit");
        cmd.args(&[
            "config",
            "import",
            "--config",
            config_path.to_str().unwrap(),
            "--file",
            import_path.to_str().unwrap(),
        ]);

        cmd.assert()
            .success()
            .stdout(predicate::str::contains("Configuration imported"));

        let mut cmd = cargo_bin_cmd!("quicommit");
        cmd.args(&[
            "config",
            "get",
            "llm.provider",
            "--config",
            config_path.to_str().unwrap(),
        ]);
        cmd.assert()
            .success()
            .stdout(predicate::str::contains("openai"));
    }

    #[test]
    fn test_import_encrypted_config() {
        let temp_dir = TempDir::new().unwrap();
        let config_path1 = temp_dir.path().join("config1.toml");
        let config_path2 = temp_dir.path().join("config2.toml");
        let export_path = temp_dir.path().join("encrypted.toml");

        init_quicommit(&config_path1);

        let mut cmd = cargo_bin_cmd!("quicommit");
        cmd.args(&[
            "config",
            "set",
            "llm.provider",
            "anthropic",
            "--config",
            config_path1.to_str().unwrap(),
        ]);
        cmd.assert().success();

        let mut cmd = cargo_bin_cmd!("quicommit");
        cmd.args(&[
            "config",
            "export",
            "--config",
            config_path1.to_str().unwrap(),
            "--output",
            export_path.to_str().unwrap(),
            "--password",
            "secure_password",
        ]);
        cmd.assert().success();

        let mut cmd = cargo_bin_cmd!("quicommit");
        cmd.args(&[
            "config",
            "import",
            "--config",
            config_path2.to_str().unwrap(),
            "--file",
            export_path.to_str().unwrap(),
            "--password",
            "secure_password",
        ]);
        cmd.assert()
            .success()
            .stdout(predicate::str::contains("Configuration imported"));

        let mut cmd = cargo_bin_cmd!("quicommit");
        cmd.args(&[
            "config",
            "get",
            "llm.provider",
            "--config",
            config_path2.to_str().unwrap(),
        ]);
        cmd.assert()
            .success()
            .stdout(predicate::str::contains("anthropic"));
    }

    #[test]
    fn test_import_encrypted_wrong_password() {
        let temp_dir = TempDir::new().unwrap();
        let config_path = temp_dir.path().join("config.toml");
        let export_path = temp_dir.path().join("encrypted.toml");

        init_quicommit(&config_path);

        let mut cmd = cargo_bin_cmd!("quicommit");
        cmd.args(&[
            "config",
            "export",
            "--config",
            config_path.to_str().unwrap(),
            "--output",
            export_path.to_str().unwrap(),
            "--password",
            "correct_password",
        ]);
        cmd.assert().success();

        let mut cmd = cargo_bin_cmd!("quicommit");
        cmd.args(&[
            "config",
            "import",
            "--config",
            config_path.to_str().unwrap(),
            "--file",
            export_path.to_str().unwrap(),
            "--password",
            "wrong_password",
        ]);
        cmd.assert()
            .failure()
            .stderr(predicate::str::contains("Failed to decrypt"));
    }
}

mod config_export_import_roundtrip {
    use super::*;

    #[test]
    fn test_roundtrip_plain() {
        let temp_dir = TempDir::new().unwrap();
        let config_path1 = temp_dir.path().join("config1.toml");
        let config_path2 = temp_dir.path().join("config2.toml");
        let export_path = temp_dir.path().join("export.toml");

        init_quicommit(&config_path1);

        let mut cmd = cargo_bin_cmd!("quicommit");
        cmd.args(&[
            "config",
            "set",
            "llm.model",
            "gpt-4-turbo",
            "--config",
            config_path1.to_str().unwrap(),
        ]);
        cmd.assert().success();

        let mut cmd = cargo_bin_cmd!("quicommit");
        cmd.args(&[
            "config",
            "export",
            "--config",
            config_path1.to_str().unwrap(),
            "--output",
            export_path.to_str().unwrap(),
            "--password",
            "",
        ]);
        cmd.assert().success();

        let mut cmd = cargo_bin_cmd!("quicommit");
        cmd.args(&[
            "config",
            "import",
            "--config",
            config_path2.to_str().unwrap(),
            "--file",
            export_path.to_str().unwrap(),
        ]);
        cmd.assert().success();

        let mut cmd = cargo_bin_cmd!("quicommit");
        cmd.args(&[
            "config",
            "get",
            "llm.model",
            "--config",
            config_path2.to_str().unwrap(),
        ]);
        cmd.assert()
            .success()
            .stdout(predicate::str::contains("gpt-4-turbo"));
    }

    #[test]
    fn test_roundtrip_encrypted() {
        let temp_dir = TempDir::new().unwrap();
        let config_path1 = temp_dir.path().join("config1.toml");
        let config_path2 = temp_dir.path().join("config2.toml");
        let export_path = temp_dir.path().join("encrypted.toml");
        let password = "my_secure_password_123";

        init_quicommit(&config_path1);

        let mut cmd = cargo_bin_cmd!("quicommit");
        cmd.args(&[
            "config",
            "set",
            "llm.provider",
            "deepseek",
            "--config",
            config_path1.to_str().unwrap(),
        ]);
        cmd.assert().success();

        let mut cmd = cargo_bin_cmd!("quicommit");
        cmd.args(&[
            "config",
            "set",
            "llm.model",
            "deepseek-chat",
            "--config",
            config_path1.to_str().unwrap(),
        ]);
        cmd.assert().success();

        let mut cmd = cargo_bin_cmd!("quicommit");
        cmd.args(&[
            "config",
            "export",
            "--config",
            config_path1.to_str().unwrap(),
            "--output",
            export_path.to_str().unwrap(),
            "--password",
            password,
        ]);
        cmd.assert().success();

        let exported_content = fs::read_to_string(&export_path).unwrap();
        assert!(exported_content.starts_with("ENCRYPTED:"));
        assert!(!exported_content.contains("deepseek"));

        let mut cmd = cargo_bin_cmd!("quicommit");
        cmd.args(&[
            "config",
            "import",
            "--config",
            config_path2.to_str().unwrap(),
            "--file",
            export_path.to_str().unwrap(),
            "--password",
            password,
        ]);
        cmd.assert().success();

        let mut cmd = cargo_bin_cmd!("quicommit");
        cmd.args(&[
            "config",
            "get",
            "llm.provider",
            "--config",
            config_path2.to_str().unwrap(),
        ]);
        cmd.assert()
            .success()
            .stdout(predicate::str::contains("deepseek"));

        let mut cmd = cargo_bin_cmd!("quicommit");
        cmd.args(&[
            "config",
            "get",
            "llm.model",
            "--config",
            config_path2.to_str().unwrap(),
        ]);
        cmd.assert()
            .success()
            .stdout(predicate::str::contains("deepseek-chat"));
    }
}
