use std::collections::HashMap;
use std::io::Write;
use closure_app::core::agent::{Agent, ClaudeCodeAdapter, CodexAdapter};
use closure_app::core::provider::Provider;
use tempfile::NamedTempFile;

#[test]
fn test_claude_code_apply() {
    let mut file = NamedTempFile::new().unwrap();
    let initial = r#"{
  "model": "old-model",
  "env": {"OTHER_VAR": "value"},
  "tui": "fullscreen"
}"#;
    file.write_all(initial.as_bytes()).unwrap();

    let mut env = HashMap::new();
    env.insert("ANTHROPIC_AUTH_TOKEN".to_string(), "sk-new".to_string());
    env.insert("ANTHROPIC_BASE_URL".to_string(), "https://new".to_string());

    let provider = Provider::ClaudeCode {
        name: "test".to_string(),
        model: Some("kimi-for-coding".to_string()),
        env,
    };

    let adapter = ClaudeCodeAdapter::new(file.path());
    adapter.apply(&provider).unwrap();

    let contents = std::fs::read_to_string(file.path()).unwrap();
    let json: serde_json::Value = serde_json::from_str(&contents).unwrap();

    assert_eq!(json["model"], "kimi-for-coding");
    assert_eq!(json["env"]["ANTHROPIC_AUTH_TOKEN"], "sk-new");
    assert_eq!(json["env"]["ANTHROPIC_BASE_URL"], "https://new");
    assert_eq!(json["tui"], "fullscreen"); // preserved
}

#[test]
fn test_codex_apply() {
    let mut config_file = NamedTempFile::new().unwrap();
    let auth_file = NamedTempFile::new().unwrap();

    let initial_config = r#"model = "old-model"
model_provider = "OldProvider"
review_model = "old-review"

[model_providers.OldProvider]
base_url = "https://old"
"#;
    config_file.write_all(initial_config.as_bytes()).unwrap();

    let provider = Provider::Codex {
        name: "test".to_string(),
        api_key: Some("sk-new".to_string()),
        base_url: Some("https://new".to_string()),
        model: Some("gpt-5.5".to_string()),
        model_provider: Some("OpenAI".to_string()),
        review_model: Some("gpt-5.4".to_string()),
    };

    let adapter = CodexAdapter::new(config_file.path(), auth_file.path());
    adapter.apply(&provider).unwrap();

    let config_contents = std::fs::read_to_string(config_file.path()).unwrap();
    let config: toml::Table = config_contents.parse().unwrap();

    assert_eq!(config["model"].as_str(), Some("gpt-5.5"));
    assert_eq!(config["model_provider"].as_str(), Some("OpenAI"));
    assert_eq!(config["review_model"].as_str(), Some("gpt-5.4"));
    assert_eq!(
        config["model_providers"]["OpenAI"]["base_url"].as_str(),
        Some("https://new")
    );

    let auth_contents = std::fs::read_to_string(auth_file.path()).unwrap();
    let auth: serde_json::Value = serde_json::from_str(&auth_contents).unwrap();
    assert_eq!(auth["OPENAI_API_KEY"], "sk-new");
}

#[test]
fn test_full_flow_select_and_apply() {
    use closure_app::core::agent::{Agent, ClaudeCodeAdapter};
    use closure_app::core::provider::{Provider, ProviderStore};
    use closure_app::core::store::Store;
    use std::collections::HashMap;
    use tempfile::TempDir;

    let tmp = TempDir::new().unwrap();
    let store = Store::new(tmp.path());

    let mut env = HashMap::new();
    env.insert("ANTHROPIC_AUTH_TOKEN".to_string(), "sk-test".to_string());
    let data = ProviderStore {
        version: 1,
        providers: vec![
            Provider::ClaudeCode {
                name: "test".to_string(),
                model: Some("kimi".to_string()),
                env,
            }
        ],
    };
    store.save_encrypted(&data, "password").unwrap();

    store.authenticate("password").unwrap();
    assert!(store.is_authenticated());

    let loaded = store.load().unwrap();
    assert_eq!(loaded.providers.len(), 1);

    let config_path = tmp.path().join("settings.json");
    let adapter = ClaudeCodeAdapter::new(&config_path);
    adapter.apply(&loaded.providers[0]).unwrap();

    let contents = std::fs::read_to_string(&config_path).unwrap();
    assert!(contents.contains("kimi"));
    assert!(contents.contains("sk-test"));
}

#[test]
fn test_provider_update_claude_code() {
    use closure_app::core::provider::Provider;
    use std::collections::HashMap;

    let env = HashMap::from([
        ("KEY_A".to_string(), "val_a".to_string()),
    ]);
    let mut p = Provider::ClaudeCode {
        name: "test".to_string(),
        model: Some("old-model".to_string()),
        env,
    };

    p.update(
        Some("new-model"), None, None, None, None,
        &[("KEY_B".to_string(), "val_b".to_string())],
    );

    match p {
        Provider::ClaudeCode { model, env, .. } => {
            assert_eq!(model, Some("new-model".to_string()));
            assert_eq!(env.get("KEY_A"), Some(&"val_a".to_string()));
            assert_eq!(env.get("KEY_B"), Some(&"val_b".to_string()));
        }
        _ => panic!("wrong variant"),
    }
}

#[test]
fn test_provider_update_codex() {
    use closure_app::core::provider::Provider;

    let mut p = Provider::Codex {
        name: "test".to_string(),
        api_key: Some("old-key".to_string()),
        base_url: Some("https://old".to_string()),
        model: Some("old-model".to_string()),
        model_provider: Some("OldProvider".to_string()),
        review_model: None,
    };

    p.update(
        Some("new-model"), Some("new-key"), Some("https://new"),
        None, Some("new-review"), &[],
    );

    match p {
        Provider::Codex { api_key, base_url, model, model_provider, review_model, .. } => {
            assert_eq!(api_key, Some("new-key".to_string()));
            assert_eq!(base_url, Some("https://new".to_string()));
            assert_eq!(model, Some("new-model".to_string()));
            assert_eq!(model_provider, Some("OldProvider".to_string())); // unchanged
            assert_eq!(review_model, Some("new-review".to_string()));
        }
        _ => panic!("wrong variant"),
    }
}

#[test]
fn test_store_add_and_remove_provider() {
    use closure_app::core::provider::{Provider, ProviderStore};
    use closure_app::core::store::Store;
    use std::collections::HashMap;
    use tempfile::TempDir;

    let tmp = TempDir::new().unwrap();
    let store = Store::new(tmp.path());
    store.save(&ProviderStore::default()).unwrap();

    // Add a provider
    let mut store_data = store.load().unwrap();
    let env = HashMap::from([
        ("ANTHROPIC_AUTH_TOKEN".to_string(), "sk-xxx".to_string()),
    ]);
    store_data.providers.push(Provider::ClaudeCode {
        name: "my-provider".to_string(),
        model: Some("kimi".to_string()),
        env,
    });
    store.save(&store_data).unwrap();

    let loaded = store.load().unwrap();
    assert_eq!(loaded.providers.len(), 1);
    assert_eq!(loaded.providers[0].name(), "my-provider");

    // Remove it
    let mut store_data = store.load().unwrap();
    let idx = store_data.providers.iter()
        .position(|p| p.name() == "my-provider")
        .unwrap();
    store_data.providers.remove(idx);
    store.save(&store_data).unwrap();

    let loaded = store.load().unwrap();
    assert_eq!(loaded.providers.len(), 0);
}
