use crate::db;
use git2::{IndexAddOption, Repository, Signature};
use rusqlite::params;
use serde::Serialize;
use std::path::Path;

// ── WSL 路径转换 ──────────────────────────────────────────────────────────────

/// 从 settings 表读取 wsl_distro（默认 Ubuntu）
fn get_wsl_distro() -> String {
    let conn = match db::pool().get() {
        Ok(c) => c,
        Err(_) => return "Ubuntu".to_string(),
    };
    conn.query_row(
        "SELECT value FROM settings WHERE key = 'wsl_distro'",
        [],
        |row| row.get::<_, String>(0),
    )
    .unwrap_or_else(|_| "Ubuntu".to_string())
}

/// 将 Linux 路径（/home/...）转换为 Windows UNC 路径（\\wsl.localhost\Ubuntu\home\...）
/// 非 Linux 路径原样返回
pub fn wsl_to_win(linux_path: &str) -> String {
    if linux_path.starts_with('/') {
        let distro = get_wsl_distro();
        let inner = linux_path.trim_start_matches('/').replace('/', "\\");
        format!("\\\\wsl.localhost\\{}\\{}", distro, inner)
    } else {
        linux_path.to_string()
    }
}

/// 暴露给前端：将 Linux 路径转换为 Windows 可访问的 UNC 路径
#[tauri::command]
pub fn resolve_wsl_path(path: String) -> String {
    wsl_to_win(&path)
}

fn open_repo(path: &str) -> Result<Repository, String> {
    Repository::open(path).map_err(|e| format!("无法打开仓库: {e}"))
}

/// 列出当前系统已安装的 WSL 发行版（wsl -l -q）
#[tauri::command]
pub fn get_wsl_distros() -> Result<Vec<String>, String> {
    let output = crate::silent_cmd("wsl")
        .args(["-l", "-q"])
        .output()
        .map_err(|_| "未检测到 WSL，请确认已安装 WSL 2".to_string())?;

    if !output.status.success() && output.stdout.is_empty() {
        return Err("WSL 未安装或未启用".to_string());
    }

    // wsl -l -q 在 Windows 上输出 UTF-16 LE（带或不带 BOM）
    let bytes = &output.stdout;
    let text = if bytes.len() >= 2 && bytes[0] == 0xFF && bytes[1] == 0xFE {
        // UTF-16 LE with BOM
        let words: Vec<u16> = bytes[2..]
            .chunks(2)
            .filter(|c| c.len() == 2)
            .map(|c| u16::from_le_bytes([c[0], c[1]]))
            .collect();
        String::from_utf16_lossy(&words).to_string()
    } else if bytes.windows(2).any(|w| w[1] == 0) {
        // UTF-16 LE without BOM（每个字符后跟 0x00）
        let words: Vec<u16> = bytes
            .chunks(2)
            .filter(|c| c.len() == 2)
            .map(|c| u16::from_le_bytes([c[0], c[1]]))
            .collect();
        String::from_utf16_lossy(&words).to_string()
    } else {
        String::from_utf8_lossy(bytes).to_string()
    };

    let distros: Vec<String> = text
        .lines()
        .map(|l| l.trim_matches(|c: char| c.is_whitespace() || c == '\0').to_string())
        .filter(|l| !l.is_empty())
        .collect();

    if distros.is_empty() {
        return Err("未找到已安装的 WSL 发行版".to_string());
    }

    Ok(distros)
}

// ── 数据结构 ─────────────────────────────────────────────────────────────────

#[derive(Serialize, Clone)]
pub struct ChangedFile {
    pub status: String, // "M" 修改 / "A" 新增 / "D" 删除 / "?" 未跟踪
    pub path: String,
}

#[derive(Serialize, Clone)]
pub struct GitRemote {
    pub name: String,
    pub url: String,
}

#[derive(Serialize)]
pub struct GitScanResult {
    pub has_git: bool,
    pub has_commits: bool,
    pub branch: String,
    pub has_changes: bool,
    pub changed_files: Vec<ChangedFile>,
    pub remotes: Vec<GitRemote>,
    pub has_gitignore: bool,
    pub suspicious_files: Vec<String>,
    pub suggested_templates: Vec<String>, // 推荐 .gitignore 模板
}

// ── 内部工具 ─────────────────────────────────────────────────────────────────

/// 检测疑似敏感文件（只扫一层 + 常见子目录，避免遍历 node_modules）
fn scan_suspicious(root: &Path) -> Vec<String> {
    let patterns = [
        ".env", ".env.local", ".env.production", ".env.development",
        "secrets.json", "credentials.json", "credentials.yml",
        "config/database.yml", "config/secrets.yml",
    ];
    let ext_patterns = ["key", "pem", "p12", "pfx", "cer", "crt"];

    let mut found = Vec::new();

    let check_file = |rel: &str, found: &mut Vec<String>| {
        let lower = rel.to_lowercase();
        // 固定名称
        for p in &patterns {
            if lower == *p || lower.ends_with(&format!("/{}", p)) {
                found.push(rel.to_string());
                return;
            }
        }
        // 扩展名
        if let Some(ext) = rel.rsplit('.').next() {
            if ext_patterns.contains(&ext.to_lowercase().as_str()) {
                found.push(rel.to_string());
            }
        }
        // .env.* 通配
        let filename = rel.rsplit('/').next().unwrap_or(rel);
        if filename.starts_with(".env.") || filename.starts_with("*.env") {
            found.push(rel.to_string());
        }
    };

    // 只扫根目录一层
    if let Ok(entries) = std::fs::read_dir(root) {
        for entry in entries.flatten() {
            let name = entry.file_name().to_string_lossy().to_string();
            // 跳过 .git / node_modules / target 等大型目录
            if matches!(name.as_str(), ".git" | "node_modules" | "target" | "dist" | ".next") {
                continue;
            }
            if entry.path().is_file() {
                check_file(&name, &mut found);
            }
        }
    }

    found
}

/// 根据项目文件推断 .gitignore 模板
fn detect_templates(root: &Path) -> Vec<String> {
    let mut templates = Vec::new();
    let markers: &[(&str, &str)] = &[
        ("package.json",      "Node"),
        ("Cargo.toml",        "Rust"),
        ("requirements.txt",  "Python"),
        ("pyproject.toml",    "Python"),
        ("go.mod",            "Go"),
        ("pom.xml",           "Java"),
        ("build.gradle",      "Java"),
        ("*.csproj",          "VisualStudio"),
        ("*.sln",             "VisualStudio"),
    ];
    for (file, template) in markers {
        let path = root.join(file);
        if path.exists() && !templates.contains(&template.to_string()) {
            templates.push(template.to_string());
        }
    }
    // 通配符检查 *.csproj / *.sln
    if !templates.contains(&"VisualStudio".to_string()) {
        if let Ok(entries) = std::fs::read_dir(root) {
            for entry in entries.flatten() {
                let name = entry.file_name().to_string_lossy().to_string();
                if name.ends_with(".csproj") || name.ends_with(".sln") {
                    templates.push("VisualStudio".to_string());
                    break;
                }
            }
        }
    }
    templates
}

// ── WSL git 辅助 ──────────────────────────────────────────────────────────────

/// 从原始路径（可能是 Linux 路径或 UNC 路径）提取 Linux 绝对路径
/// - 原始路径以 `/` 开头 → 直接返回
/// - win_path 是 `\\wsl.localhost\Distro\...` → 提取 `/...` 部分
pub fn extract_linux_path(original: &str, win_path: &str) -> Option<String> {
    if original.starts_with('/') {
        return Some(original.to_string());
    }
    // \\wsl.localhost\Distro\home\user\... → /home/user/...
    if win_path.starts_with("\\\\wsl.localhost\\") {
        let rest = &win_path["\\\\wsl.localhost\\".len()..];
        if let Some(slash_idx) = rest.find('\\') {
            let linux = "/".to_string() + &rest[slash_idx + 1..].replace('\\', "/");
            return Some(linux);
        }
    }
    None
}

/// 在 WSL 中执行 git 命令，返回 stdout 字符串
fn run_wsl_git(linux_path: &str, git_args: &[&str]) -> Result<String, String> {
    let mut args: Vec<&str> = vec!["git", "-C", linux_path];
    args.extend_from_slice(git_args);
    let output = crate::silent_cmd("wsl")
        .args(&args)
        .output()
        .map_err(|e| format!("wsl 命令失败: {e}"))?;
    if !output.status.success() {
        let stderr = String::from_utf8_lossy(&output.stderr).to_string();
        return Err(if stderr.is_empty() {
            format!("git 命令退出码: {}", output.status)
        } else {
            stderr
        });
    }
    Ok(String::from_utf8_lossy(&output.stdout).to_string())
}

/// WSL 项目专用扫描：通过 `wsl git` 获取准确状态
/// 避免 libgit2 在 Windows UNC 路径下 .gitignore 规则失效（路径分隔符不匹配）
fn scan_local_git_via_wsl(linux_path: &str, win_path: &str) -> Result<GitScanResult, String> {
    let root = Path::new(win_path);

    let has_gitignore = root.join(".gitignore").exists();
    let suspicious_files = scan_suspicious(root);
    let suggested_templates = detect_templates(root);

    if !root.join(".git").exists() {
        return Ok(GitScanResult {
            has_git: false,
            has_commits: false,
            branch: String::new(),
            has_changes: false,
            changed_files: vec![],
            remotes: vec![],
            has_gitignore,
            suspicious_files,
            suggested_templates,
        });
    }

    // 分支名
    let branch = run_wsl_git(linux_path, &["branch", "--show-current"])
        .unwrap_or_default()
        .trim()
        .to_string();
    let branch = if branch.is_empty() {
        // 可能是 detached HEAD，尝试 rev-parse --abbrev-ref
        run_wsl_git(linux_path, &["rev-parse", "--abbrev-ref", "HEAD"])
            .unwrap_or_default()
            .trim()
            .to_string()
    } else {
        branch
    };
    let branch = if branch.is_empty() || branch == "HEAD" {
        "main".to_string()
    } else {
        branch
    };

    // 是否有提交
    let has_commits = run_wsl_git(linux_path, &["rev-parse", "HEAD"]).is_ok();

    // git status --porcelain（与 VSCode 使用相同的 git，gitignore 规则完全一致）
    let status_out = run_wsl_git(linux_path, &["status", "--porcelain"]).unwrap_or_default();
    let changed_files: Vec<ChangedFile> = status_out
        .lines()
        .filter(|l| l.len() >= 3)
        .map(|line| {
            let xy = &line[0..2];
            // 去掉 git 对含空格路径加的引号
            let path = line[3..].trim().trim_matches('"').to_string();
            let status = if xy.contains('?') {
                "?"
            } else if xy.contains('D') {
                "D"
            } else if xy.contains('R') {
                "R"
            } else {
                "M"
            };
            ChangedFile { status: status.to_string(), path }
        })
        .collect();

    let has_changes = !changed_files.is_empty();

    // git remote -v
    let remote_out = run_wsl_git(linux_path, &["remote", "-v"]).unwrap_or_default();
    let mut remotes: Vec<GitRemote> = Vec::new();
    for line in remote_out.lines() {
        if !line.ends_with("(fetch)") {
            continue;
        }
        let mut parts = line.splitn(2, '\t');
        let name = parts.next().unwrap_or("").to_string();
        let url = parts
            .next()
            .and_then(|s| s.split_whitespace().next())
            .unwrap_or("")
            .to_string();
        if !name.is_empty() && !url.is_empty() {
            remotes.push(GitRemote { name, url });
        }
    }

    Ok(GitScanResult {
        has_git: true,
        has_commits,
        branch,
        has_changes,
        changed_files,
        remotes,
        has_gitignore,
        suspicious_files,
        suggested_templates,
    })
}

// ── Tauri 命令 ────────────────────────────────────────────────────────────────

/// 扫描本地目录的 git 状态、敏感文件、技术栈
#[tauri::command]
pub fn scan_local_git(path: String) -> Result<GitScanResult, String> {
    // Linux 路径自动转换为 Windows UNC 路径
    let win_path = wsl_to_win(&path);
    let root = Path::new(&win_path);

    if !root.exists() {
        return Err(format!(
            "路径不存在: {}\n（WSL 路径已转换为: {}）\n请确认设置中的 WSL 发行版名称是否正确",
            path, win_path
        ));
    }

    // WSL 项目：通过 wsl git 命令扫描，保证 .gitignore 规则与 VSCode 完全一致
    if let Some(linux_path) = extract_linux_path(&path, &win_path) {
        return scan_local_git_via_wsl(&linux_path, &win_path);
    }

    // Windows 项目：使用 libgit2
    let has_gitignore = root.join(".gitignore").exists();
    let suspicious_files = scan_suspicious(root);
    let suggested_templates = detect_templates(root);

    let git_dir = root.join(".git");
    if !git_dir.exists() {
        return Ok(GitScanResult {
            has_git: false,
            has_commits: false,
            branch: String::new(),
            has_changes: false,
            changed_files: vec![],
            remotes: vec![],
            has_gitignore,
            suspicious_files,
            suggested_templates,
        });
    }

    let repo = open_repo(&root.to_string_lossy())?;

    // 分支名
    let branch = repo
        .head()
        .ok()
        .and_then(|h| h.shorthand().map(|s| s.to_string()))
        .unwrap_or_else(|| "main".to_string());

    // 是否有提交
    let has_commits = repo.head().is_ok();

    // 改动文件
    let mut changed_files: Vec<ChangedFile> = Vec::new();
    let statuses = repo
        .statuses(Some(
            git2::StatusOptions::new()
                .include_untracked(true)
                .recurse_untracked_dirs(false),
        ))
        .map_err(|e| e.to_string())?;

    for entry in statuses.iter() {
        let s = entry.status();
        let status_str = if s.contains(git2::Status::INDEX_NEW) || s.contains(git2::Status::WT_NEW) {
            "?"
        } else if s.contains(git2::Status::INDEX_MODIFIED) || s.contains(git2::Status::WT_MODIFIED) {
            "M"
        } else if s.contains(git2::Status::INDEX_DELETED) || s.contains(git2::Status::WT_DELETED) {
            "D"
        } else if s.contains(git2::Status::INDEX_RENAMED) || s.contains(git2::Status::WT_RENAMED) {
            "R"
        } else {
            "M"
        };

        if let Some(path_str) = entry.path() {
            changed_files.push(ChangedFile {
                status: status_str.to_string(),
                path: path_str.to_string(),
            });
        }
    }

    let has_changes = !changed_files.is_empty();

    // Remote 列表
    let remotes: Vec<GitRemote> = repo
        .remotes()
        .map(|names| {
            names
                .iter()
                .flatten()
                .filter_map(|name| {
                    repo.find_remote(name).ok().map(|r| GitRemote {
                        name: name.to_string(),
                        url: r.url().unwrap_or("").to_string(),
                    })
                })
                .collect()
        })
        .unwrap_or_default();

    Ok(GitScanResult {
        has_git: true,
        has_commits,
        branch,
        has_changes,
        changed_files,
        remotes,
        has_gitignore,
        suspicious_files,
        suggested_templates,
    })
}

/// git init（若还没有 .git）并提交所有改动
#[tauri::command]
pub fn git_init_and_commit(path: String, message: String) -> Result<(), String> {
    let win_path = wsl_to_win(&path);
    let root = Path::new(&win_path);
    let repo = if root.join(".git").exists() {
        open_repo(&root.to_string_lossy())?
    } else {
        Repository::init(root).map_err(|e| format!("git init 失败: {e}"))?
    };

    let mut index = repo.index().map_err(|e| e.to_string())?;
    index
        .add_all(["."].iter(), IndexAddOption::DEFAULT, None)
        .map_err(|e| format!("git add 失败: {e}"))?;
    index.write().map_err(|e| e.to_string())?;

    let tree_oid = index.write_tree().map_err(|e| e.to_string())?;
    let tree = repo.find_tree(tree_oid).map_err(|e| e.to_string())?;

    let sig = Signature::now("dev-manager", "dev@local")
        .map_err(|e| e.to_string())?;

    let parents: Vec<git2::Commit> = if let Ok(head) = repo.head() {
        if let Ok(commit) = head.peel_to_commit() {
            vec![commit]
        } else {
            vec![]
        }
    } else {
        vec![]
    };
    let parent_refs: Vec<&git2::Commit> = parents.iter().collect();

    repo.commit(Some("HEAD"), &sig, &sig, &message, &tree, &parent_refs)
        .map_err(|e| format!("commit 失败: {e}"))?;

    Ok(())
}

/// 添加或更新 origin remote
#[tauri::command]
pub fn git_remote_set(path: String, url: String) -> Result<(), String> {
    let win_path = wsl_to_win(&path);
    let repo = open_repo(&win_path)?;
    let has_origin = repo.find_remote("origin").is_ok();
    if has_origin {
        repo.remote_set_url("origin", &url)
            .map_err(|e| format!("set-url 失败: {e}"))
    } else {
        repo.remote("origin", &url)
            .map(|_| ())
            .map_err(|e| format!("remote add 失败: {e}"))
    }
}

/// 写入 .gitignore 文件到指定目录
#[tauri::command]
pub fn write_gitignore(dir: String, content: String) -> Result<(), String> {
    let win_dir = wsl_to_win(&dir);
    let path = Path::new(&win_dir).join(".gitignore");
    std::fs::write(&path, content).map_err(|e| format!("写入 .gitignore 失败: {e}"))
}

/// 注册到 git_repos 并更新 project_workspaces.repo_id
#[tauri::command]
pub fn register_and_link_repo(
    project_id: String,
    repo_url: String,
    name: String,
    description: Option<String>,
    default_branch: Option<String>,
) -> Result<(), String> {
    let conn = db::pool().get().map_err(|e| e.to_string())?;

    let repo_id = uuid::Uuid::new_v4().to_string();
    let branch = default_branch.as_deref().unwrap_or("main");

    // 插入 git_repos（同 URL 已存在则忽略）
    conn.execute(
        "INSERT OR IGNORE INTO git_repos (id, repo_url, name, description, default_branch)
         VALUES (?1, ?2, ?3, ?4, ?5)",
        params![repo_id, repo_url, name, description, branch],
    )
    .map_err(|e| e.to_string())?;

    // 取实际 id（可能已存在）
    let actual_id: String = conn
        .query_row(
            "SELECT id FROM git_repos WHERE repo_url = ?1",
            params![repo_url],
            |r| r.get(0),
        )
        .map_err(|e| format!("查询 git_repos 失败: {e}"))?;

    // 更新 project_workspaces.repo_id 及 profile 的 repo_url
    conn.execute(
        "UPDATE project_workspaces SET repo_id = ?1 WHERE id = ?2",
        params![actual_id, project_id],
    )
    .map_err(|e| e.to_string())?;

    conn.execute(
        "UPDATE project_profiles SET repo_url = ?1 WHERE id = (
             SELECT profile_id FROM project_workspaces WHERE id = ?2
         )",
        params![repo_url, project_id],
    )
    .map_err(|e| e.to_string())?;

    Ok(())
}
