#!/usr/bin/env bash

set -euo pipefail

TESTS_DIR="$(cd "$(dirname "$0")" && pwd)"
REPO_ROOT="$(cd "${TESTS_DIR}/.." && pwd)"
UNIT_TEST_TIMEOUT_SECONDS="${UNIT_TEST_TIMEOUT_SECONDS:-1200}"
RUST_TEST_THREADS="${RUST_TEST_THREADS:-1}"
MIN_AVG_COVERAGE="${MIN_AVG_COVERAGE:-70}"
SONAR_XML="${SONAR_XML:-${REPO_ROOT}/kylin-patch-coverage.xml}"
COBERTURA_XML="${REPO_ROOT}/target/kylin-patch-cobertura.xml"
LOCAL_CARGO_HOME="${CARGO_HOME:-${REPO_ROOT}/.cargo-test-home}"
LOCAL_RUSTUP_HOME="${RUSTUP_HOME:-${REPO_ROOT}/.rustup-test-home}"
LOCAL_LLVM_BIN="${REPO_ROOT}/.llvm-bin"

export CARGO_HOME="${LOCAL_CARGO_HOME}"
export RUSTUP_HOME="${LOCAL_RUSTUP_HOME}"
export PATH="/usr/bin:${CARGO_HOME}/bin:${LOCAL_LLVM_BIN}:${HOME}/.cargo/bin:${PATH}"
export RUST_TEST_THREADS

ensure_command() {
    local cmd="$1"
    local hint="$2"
    if ! command -v "${cmd}" >/dev/null 2>&1; then
        echo "错误: 未找到 ${cmd}，${hint}" >&2
        exit 1
    fi
}

find_llvm_tool() {
    local base="$1"
    local candidate

    if command -v "${base}" >/dev/null 2>&1; then
        command -v "${base}"
        return 0
    fi

    for candidate in \
        "${LOCAL_LLVM_BIN}/${base}" \
        /usr/bin/"${base}" \
        /usr/bin/"${base}"-* \
        /usr/lib/llvm-*/bin/"${base}" \
        /usr/local/bin/"${base}"-*; do
        if [ -x "${candidate}" ]; then
            echo "${candidate}"
            return 0
        fi
    done

    return 1
}

ensure_llvm_backend() {
    local llvm_cov_bin=""
    local llvm_profdata_bin=""

    llvm_cov_bin="$(find_llvm_tool llvm-cov || true)"
    llvm_profdata_bin="$(find_llvm_tool llvm-profdata || true)"

    if [ -n "${llvm_cov_bin}" ] && [ -n "${llvm_profdata_bin}" ]; then
        export LLVM_COV="${llvm_cov_bin}"
        export LLVM_PROFDATA="${llvm_profdata_bin}"
        return 0
    fi

    return 1
}

read_coverage_metrics() {
    python3 - "${SONAR_XML}" <<'PY'
import sys
import xml.etree.ElementTree as ET

path = sys.argv[1]
root = ET.parse(path).getroot()
lines = root.findall(".//lineToCover")
branches_valid = 0
branches_covered = 0
lines_valid = len(lines)
lines_covered = 0

for line in lines:
    if line.attrib.get("covered") == "true":
        lines_covered += 1
    covered_branches = line.attrib.get("coveredBranches")
    branches_to_cover = line.attrib.get("branchesToCover")
    if covered_branches is not None and branches_to_cover is not None:
        try:
            branches_covered += int(covered_branches)
            branches_valid += int(branches_to_cover)
        except Exception:
            pass

line_rate = (lines_covered / lines_valid * 100.0) if lines_valid else 0.0
branch_rate = (branches_covered / branches_valid * 100.0) if branches_valid else line_rate
avg_rate = (line_rate + branch_rate) / 2.0
print(f"{line_rate:.2f} {branch_rate:.2f} {avg_rate:.2f}")
PY
}

generate_sonar_xml() {
    python3 - "${COBERTURA_XML}" "${SONAR_XML}" "${REPO_ROOT}" <<'PY'
import sys
import xml.etree.ElementTree as ET
from pathlib import Path

cobertura_path = Path(sys.argv[1])
sonar_path = Path(sys.argv[2])
repo_root = Path(sys.argv[3]).resolve()
root = ET.parse(cobertura_path).getroot()

allowed = {
    "src/main.rs",
    "src/patcher.rs",
}

coverage = ET.Element("coverage", version="1")
seen = set()

for cls in root.findall(".//class"):
    filename = cls.attrib.get("filename", "")
    try:
        path_obj = Path(filename)
        if path_obj.is_absolute():
            rel = path_obj.resolve().relative_to(repo_root).as_posix()
        else:
            rel = path_obj.as_posix()
    except Exception:
        continue
    if rel not in allowed or rel in seen:
        continue
    seen.add(rel)

    file_el = ET.SubElement(coverage, "file", path=rel)
    line_items = {}

    for line in cls.findall("./lines/line"):
        number = line.attrib.get("number")
        if not number:
            continue
        hits = int(line.attrib.get("hits", "0"))
        attrs = {
            "lineNumber": number,
            "covered": "true" if hits > 0 else "false",
        }
        if line.attrib.get("branch") == "true":
            condition = line.attrib.get("condition-coverage", "")
            if "(" in condition and "/" in condition and ")" in condition:
                try:
                    inside = condition.split("(", 1)[1].split(")", 1)[0]
                    covered, total = inside.split("/", 1)
                    attrs["coveredBranches"] = covered
                    attrs["branchesToCover"] = total
                except Exception:
                    pass
        line_items[int(number)] = attrs

    for number in sorted(line_items):
        ET.SubElement(file_el, "lineToCover", line_items[number])

sonar_path.parent.mkdir(parents=True, exist_ok=True)
tree = ET.ElementTree(coverage)
ET.indent(tree, space="  ")
tree.write(sonar_path, encoding="utf-8", xml_declaration=True)
PY
}

ensure_command cargo "请先安装 Rust 工具链"
ensure_command python3 "请先安装 Python 3"

cd "${REPO_ROOT}"

if ! ensure_llvm_backend; then
    echo "[ERROR] 未找到可用的 llvm-cov / llvm-profdata。" >&2
    echo "[ERROR] 请先通过 tests/auto_test.sh 准备覆盖率工具链后重试。" >&2
    exit 1
fi

echo "[INFO] 清理旧覆盖率数据..."
rm -f "${SONAR_XML}" "${COBERTURA_XML}"

echo "[INFO] 运行 Rust 单元测试并生成覆盖率..."
set +e
timeout --signal=TERM "${UNIT_TEST_TIMEOUT_SECONDS}s" \
    cargo llvm-cov \
    --all-targets \
    --cobertura \
    --output-path "${COBERTURA_XML}" \
    -- --nocapture --test-threads="${RUST_TEST_THREADS}"
run_ret=$?
set -e

if [ "${run_ret}" -eq 124 ]; then
    echo "[ERROR] 单元测试或覆盖率统计超时(${UNIT_TEST_TIMEOUT_SECONDS}s)" >&2
    exit 1
fi

if [ "${run_ret}" -ne 0 ]; then
    echo "[ERROR] 单元测试或覆盖率统计失败" >&2
    exit "${run_ret}"
fi

if [ ! -f "${COBERTURA_XML}" ]; then
    echo "[ERROR] 未生成覆盖率报告 ${COBERTURA_XML}" >&2
    exit 1
fi

generate_sonar_xml

read -r line_rate branch_rate avg_rate <<EOF
$(read_coverage_metrics)
EOF

echo "[INFO] 行覆盖率: ${line_rate}%"
echo "[INFO] 分支覆盖率: ${branch_rate}%"
echo "[INFO] 平均覆盖率: ${avg_rate}%"
echo "[INFO] 覆盖率 XML: ${SONAR_XML}"

python3 - "${line_rate}" "${branch_rate}" "${avg_rate}" "${MIN_AVG_COVERAGE}" <<'PY'
import sys

line_rate = float(sys.argv[1])
branch_rate = float(sys.argv[2])
avg_rate = float(sys.argv[3])
threshold = float(sys.argv[4])

if avg_rate <= threshold:
    print(
        f"[ERROR] 覆盖率门禁未通过: line={line_rate:.2f}% branch={branch_rate:.2f}% avg={avg_rate:.2f}% <= {threshold:.2f}%",
        file=sys.stderr,
    )
    sys.exit(1)
PY

echo "[INFO] 单元测试全部通过，覆盖率门禁通过"
