use std::fs;
use std::path::Path;
use tempfile::TempDir;
use serial_test::serial;
use l_tool::cli::JsCommands;
use l_tool::commands::js::commands::handle_js_command;
use l_tool::utils::package_manager::detect_package_manager;

/// 创建测试用的 package.json 文件
fn create_test_package_json(dir: &Path, content: &str) {
    let package_json_path = dir.join("package.json");
    fs::write(package_json_path, content).expect("Failed to write package.json");
}

/// 创建测试用的锁文件
fn create_lock_file(dir: &Path, lock_type: &str) {
    let lock_file = match lock_type {
        "npm" => "package-lock.json",
        "yarn" => "yarn.lock",
        "pnpm" => "pnpm-lock.yaml",
        _ => panic!("Unknown lock type"),
    };
    
    let content = match lock_type {
        "npm" => r#"{"lockfileVersion": 2}"#,
        "yarn" => "# yarn lockfile v1",
        "pnpm" => "lockfileVersion: '6.0'",
        _ => "",
    };
    
    let lock_path = dir.join(lock_file);
    fs::write(lock_path, content).expect("Failed to write lock file");
}

#[tokio::test]
#[serial]
async fn test_upgrade_with_npm() {
    let temp_dir = TempDir::new().expect("Failed to create temp dir");
    let temp_path = temp_dir.path();
    // Avoid real npm network; simulate install success
    unsafe { std::env::set_var("L_TOOL_FAKE_INSTALL", "1"); }
    
    // 创建测试 package.json
    let package_json = r#"{
  "dependencies": {
    "lodash": "^4.17.20",
    "axios": "^0.21.1"
  },
  "devDependencies": {
    "jest": "^26.6.3"
  }
}"#;
    
    create_test_package_json(temp_path, package_json);
    create_lock_file(temp_path, "npm");
    
    // 切换到临时目录
    let original_dir = std::env::current_dir().expect("Failed to get current dir");
    std::env::set_current_dir(temp_path).expect("Failed to change dir");
    
    // 执行升级命令（只升级 prod 依赖）
    let js_command = JsCommands::Upgrade { prod_only: true, dev_only: false };
    let result = handle_js_command(js_command).await;
    
    // 恢复原目录
    // Make sure temp_dir still exists before switching back
    assert!(temp_path.exists(), "Temp dir should exist before restore");
    std::env::set_current_dir(&original_dir).expect("Failed to restore dir");
    
    // 验证结果
    assert!(result.is_ok(), "Upgrade command should succeed");
}

#[tokio::test]
#[serial]
async fn test_upgrade_with_pnpm() {
    let temp_dir = TempDir::new().expect("Failed to create temp dir");
    let temp_path = temp_dir.path();
    unsafe { std::env::set_var("L_TOOL_FAKE_INSTALL", "1"); }
    
    // 创建测试 package.json
    let package_json = r#"{
  "dependencies": {
    "react": "^17.0.2"
  },
  "devDependencies": {
    "typescript": "^4.5.4"
  }
}"#;
    
    create_test_package_json(temp_path, package_json);
    create_lock_file(temp_path, "pnpm");
    
    // 切换到临时目录
    let original_dir = std::env::current_dir().expect("Failed to get current dir");
    std::env::set_current_dir(temp_path).expect("Failed to change dir");
    
    // 执行升级命令（默认升级所有依赖）
    let js_command = JsCommands::Upgrade { prod_only: false, dev_only: false };
    let result = handle_js_command(js_command).await;
    
    // 恢复原目录（确保临时目录仍存在）
    assert!(temp_path.exists(), "Temp dir should exist before restore");
    std::env::set_current_dir(&original_dir).expect("Failed to restore dir");
    
    // 验证结果
    assert!(result.is_ok(), "Upgrade command with dev should succeed");
}

#[tokio::test]
#[serial]
async fn test_upgrade_without_package_json() {
    let temp_dir = TempDir::new().expect("Failed to create temp dir");
    let temp_path = temp_dir.path();
    
    // 切换到临时目录（没有 package.json）
    let original_dir = std::env::current_dir().expect("Failed to get current dir");
    std::env::set_current_dir(temp_path).expect("Failed to change dir");
    
    // 执行升级命令
    let js_command = JsCommands::Upgrade { prod_only: false, dev_only: false };
    let result = handle_js_command(js_command).await;
    
    // 恢复原目录
    std::env::set_current_dir(&original_dir).expect("Failed to restore dir");
    
    // 验证结果应该失败
    assert!(result.is_err(), "Upgrade command should fail without package.json");
    assert!(result.unwrap_err().to_string().contains("package.json not found"));
}

#[tokio::test]
#[serial]
async fn test_upgrade_with_empty_dependencies() {
    let temp_dir = TempDir::new().expect("Failed to create temp dir");
    let temp_path = temp_dir.path();
    
    // 创建空的 package.json
    let package_json = r#"{
  "name": "test-project",
  "version": "1.0.0"
}"#;
    
    create_test_package_json(temp_path, package_json);
    create_lock_file(temp_path, "npm");
    
    // 切换到临时目录
    let original_dir = std::env::current_dir().expect("Failed to get current dir");
    std::env::set_current_dir(temp_path).expect("Failed to change dir");
    
    // 执行升级命令
    let js_command = JsCommands::Upgrade { prod_only: false, dev_only: false };
    let result = handle_js_command(js_command).await;
    
    // 恢复原目录
    std::env::set_current_dir(&original_dir).expect("Failed to restore dir");
    
    // 验证结果
    assert!(result.is_ok(), "Upgrade command should succeed even with empty dependencies");
}

#[tokio::test]
#[serial]
async fn test_package_manager_detection_from_package_json() {
    let temp_dir = TempDir::new().expect("Failed to create temp dir");
    let temp_path = temp_dir.path();
    
    // 测试 pnpm 检测
    let package_json_pnpm = r#"{
  "name": "test-pnpm-project",
  "version": "1.0.0",
  "packageManager": "pnpm@8.6.0",
  "dependencies": {
    "lodash": "^4.17.21"
  }
}"#;
    
    create_test_package_json(temp_path, package_json_pnpm);
    
    // 切换到临时目录
    let original_dir = std::env::current_dir().expect("Failed to get current dir");
    std::env::set_current_dir(temp_path).expect("Failed to change dir");
    
    // 检测包管理器
    let detected = detect_package_manager().expect("Should detect package manager");
    assert!(matches!(detected, l_tool::types::package::PackageManager::Pnpm), 
            "Should detect pnpm from packageManager field");
    
    // 恢复原目录
    std::env::set_current_dir(&original_dir).expect("Failed to restore dir");
}

#[tokio::test]
#[serial]
async fn test_upgrade_dev_only() {
    let temp_dir = TempDir::new().expect("Failed to create temp dir");
    let temp_path = temp_dir.path();
    unsafe { std::env::set_var("L_TOOL_FAKE_INSTALL", "1"); }
    
    // 创建包含 prod 和 dev 依赖的 package.json
    let package_json = r#"{
  "name": "test-dev-only",
  "version": "1.0.0",
  "dependencies": {
    "lodash": "^4.17.20"
  },
  "devDependencies": {
    "jest": "^26.6.3",
    "typescript": "^4.5.4"
  }
}"#;
    
    create_test_package_json(temp_path, package_json);
    create_lock_file(temp_path, "npm");
    
    // 切换到临时目录
    let original_dir = std::env::current_dir().expect("Failed to get current dir");
    std::env::set_current_dir(temp_path).expect("Failed to change dir");
    
    // 执行升级命令（只升级 dev 依赖）
    let js_command = JsCommands::Upgrade { prod_only: false, dev_only: true };
    let result = handle_js_command(js_command).await;
    
    // 恢复原目录
    assert!(temp_path.exists(), "Temp dir should exist before restore");
    std::env::set_current_dir(&original_dir).expect("Failed to restore dir");
    
    // 验证结果
    assert!(result.is_ok(), "Dev-only upgrade command should succeed");
}

#[tokio::test]
#[serial]
async fn test_upgrade_prod_only() {
    let temp_dir = TempDir::new().expect("Failed to create temp dir");
    let temp_path = temp_dir.path();
    unsafe { std::env::set_var("L_TOOL_FAKE_INSTALL", "1"); }
    
    // 创建包含 prod 和 dev 依赖的 package.json
    let package_json = r#"{
  "name": "test-prod-only",
  "version": "1.0.0",
  "dependencies": {
    "react": "^17.0.2",
    "axios": "^0.21.1"
  },
  "devDependencies": {
    "jest": "^26.6.3"
  }
}"#;
    
    create_test_package_json(temp_path, package_json);
    create_lock_file(temp_path, "yarn");
    
    // 切换到临时目录
    let original_dir = std::env::current_dir().expect("Failed to get current dir");
    std::env::set_current_dir(temp_path).expect("Failed to change dir");
    
    // 执行升级命令（只升级 prod 依赖）
    let js_command = JsCommands::Upgrade { prod_only: true, dev_only: false };
    let result = handle_js_command(js_command).await;
    
    // 恢复原目录
    assert!(temp_path.exists(), "Temp dir should exist before restore");
    std::env::set_current_dir(&original_dir).expect("Failed to restore dir");
    
    // 验证结果
    assert!(result.is_ok(), "Prod-only upgrade command should succeed");
}

#[tokio::test]
#[serial]
async fn test_package_manager_detection_from_lock_files() {
    let temp_dir = TempDir::new().expect("Failed to create temp dir");
    let temp_path = temp_dir.path();
    
    // 创建不包含 packageManager 字段的 package.json
    let package_json = r#"{
  "name": "test-project",
  "version": "1.0.0",
  "dependencies": {
    "react": "^18.0.0"
  }
}"#;
    
    create_test_package_json(temp_path, package_json);
    create_lock_file(temp_path, "yarn");
    
    // 切换到临时目录
    let original_dir = std::env::current_dir().expect("Failed to get current dir");
    std::env::set_current_dir(temp_path).expect("Failed to change dir");
    
    // 检测包管理器
    let detected = detect_package_manager().expect("Should detect package manager");
    assert!(matches!(detected, l_tool::types::package::PackageManager::Yarn), 
            "Should detect yarn from lock file");
    
    // 恢复原目录
    std::env::set_current_dir(&original_dir).expect("Failed to restore dir");
}

#[tokio::test]
#[serial]
async fn test_package_manager_priority_package_json_over_lock_file() {
    let temp_dir = TempDir::new().expect("Failed to create temp dir");
    let temp_path = temp_dir.path();
    
    // 创建包含 packageManager 字段的 package.json，但同时存在不同的锁文件
    let package_json = r#"{
  "name": "test-priority-project",
  "version": "1.0.0",
  "packageManager": "pnpm@8.6.0",
  "dependencies": {
    "vue": "^3.0.0"
  }
}"#;
    
    create_test_package_json(temp_path, package_json);
    // 故意创建 yarn 锁文件，但 packageManager 指定的是 pnpm
    create_lock_file(temp_path, "yarn");
    
    // 切换到临时目录
    let original_dir = std::env::current_dir().expect("Failed to get current dir");
    std::env::set_current_dir(temp_path).expect("Failed to change dir");
    
    // 检测包管理器
    let detected = detect_package_manager().expect("Should detect package manager");
    assert!(matches!(detected, l_tool::types::package::PackageManager::Pnpm), 
            "Should prioritize packageManager field over lock file");
    
    // 恢复原目录
    std::env::set_current_dir(&original_dir).expect("Failed to restore dir");
}