@echo off
setlocal enabledelayedexpansion

REM ============================================
REM 队列管理脚本 - Windows CMD 版本
REM 功能：安全审计文件队列管理
REM ============================================

REM 队列文件名
set "QUEUE_FILENAME=.queue"

REM --- 全局变量 ---
set "CMD="
set "SCAN_DIR_INPUT=."
set "SCAN_DIR_ABS="
set "QUEUE_FILE="
set "EXCLUDE_SCAN_FILES="
set "EXCLUDE_SCAN_DIRS="

REM --- 使用 %CD% 获取当前目录（更可靠）---
set "PWD=%CD%"

REM --- 参数解析 ---
if "%~1"=="" goto :usage
set "CMD=%~1"
if "%~2"=="" (
    set "SCAN_DIR_INPUT=."
) else (
    set "SCAN_DIR_INPUT=%~2"
)

REM --- 确定队列文件位置（固定在当前目录）---
set "QUEUE_FILE=%PWD%\%QUEUE_FILENAME%"

REM --- 确定扫描目录的绝对路径 ---
if "%SCAN_DIR_INPUT%"=="" (
    set "SCAN_DIR_ABS=%PWD%"
) else (
    if "%SCAN_DIR_INPUT:~0,1%"=="\" (
        set "SCAN_DIR_ABS=%SCAN_DIR_INPUT%"
    ) else (
        if "%SCAN_DIR_INPUT:~0,1%"=="/" (
            set "SCAN_DIR_ABS=%SCAN_DIR_INPUT%"
        ) else (
            set "SCAN_DIR_ABS=%PWD%\%SCAN_DIR_INPUT%"
        )
    )
)

REM --- 解析环境变量 ---
if defined EXCLUDE_SCAN_FILES set "EXCLUDE_SCAN_FILES=%EXCLUDE_SCAN_FILES%"
if defined EXCLUDE_SCAN_DIRS set "EXCLUDE_SCAN_DIRS=%EXCLUDE_SCAN_DIRS%"

REM --- 命令分发 ---
if /i "%CMD%"=="init" goto :func_init
if /i "%CMD%"=="status" goto :func_status
if /i "%CMD%"=="push" goto :func_push
if /i "%CMD%"=="pop" goto :func_pop
if /i "%CMD%"=="diff" goto :func_diff
if /i "%CMD%"=="clean" goto :func_clean

echo 错误: 未知命令 '%CMD%'
goto :usage

REM ============================================
REM 帮助信息
REM ============================================
:usage
echo 用法: %0 ^<命令^> [扫描目录]
echo.
echo 命令:
echo   init   : 扫描指定目录，生成 .queue 文件到当前工作目录
echo   status : 查看当前工作目录下队列的剩余数量
echo   push ^<路径1^> [路径2...^]: 将指定的文件/目录下文件追加到队列中（支持多个路径）
echo   pop    : 弹出并输出一个文件绝对路径
echo   diff [git diff 参数...] : 将 git diff 的结果加入队列
echo   clean  : 清理
echo.
echo 环境变量:
echo   EXCLUDE_SCAN_FILES : 设置空格分隔的 glob 模式列表，用于排除文件
echo   EXCLUDE_SCAN_DIRS  : 设置空格分隔的 glob 模式列表，用于排除目录
echo.
echo 注意:
echo   扫描目录支持相对路径。
echo   队列文件 (.queue) 始终生成在当前工作目录下。
echo   写入队列的文件路径均为绝对路径。
exit /b 1

REM ============================================
REM func_init: 扫描目录以初始化队列
REM ============================================
:func_init
echo 当前工作目录: %PWD%
echo 扫描目标目录: %SCAN_DIR_ABS%

REM 校验扫描目录是否存在
if not exist "%SCAN_DIR_ABS%" (
    echo 错误: 扫描目录 [%SCAN_DIR_INPUT%] 不存在
    exit /b 1
)

REM 打印排除规则
if defined EXCLUDE_SCAN_FILES echo 排除文件规则: %EXCLUDE_SCAN_FILES%
if defined EXCLUDE_SCAN_DIRS echo 排除目录规则: %EXCLUDE_SCAN_DIRS%

REM 清空队列文件
type nul > "%QUEUE_FILE%"

REM 构建参数并执行搜索
set "TOTAL_COUNT=0"

for /r "%SCAN_DIR_ABS%" %%f in (*) do (
    set "FILE_PATH=%%f"
    call :check_and_add_file
)

REM 排序去重
call :dedup_queue

REM 统计
for /f %%c in ('find /c /v "" ^< "%QUEUE_FILE%"') do set "TOTAL_COUNT=%%c"
echo.
echo ----------------------------------------
echo 文件总数: %TOTAL_COUNT%
echo ----------------------------------------
echo 队列文件已生成: %QUEUE_FILE%
goto :end

:check_and_add_file
REM 排除 .git 目录
echo "%FILE_PATH%" | findstr /i /c:".git" >nul && exit /b 0
REM 排除队列文件本身
if /i "%FILE_PATH%"=="%QUEUE_FILE%" exit /b 0
REM 排除文件排除模式
if defined EXCLUDE_SCAN_FILES (
    for %%p in (%EXCLUDE_SCAN_FILES%) do (
        set "EXT=%%~xf"
        if /i "!EXT!"=="%%~p" exit /b 0
    )
)
REM 排除目录排除模式
if defined EXCLUDE_SCAN_DIRS (
    for %%p in (%EXCLUDE_SCAN_DIRS%) do (
        echo "%FILE_PATH%" | findstr /i /c:"\\%%~p\\" >nul && exit /b 0
    )
)
REM 检查文件是否为空
for %%z in ("%FILE_PATH%") do if %%~zz==0 exit /b 0
REM 添加到队列
echo %FILE_PATH% >> "%QUEUE_FILE%"
set /a TOTAL_COUNT+=1
exit /b 0

:dedup_queue
REM 排序去重：使用 sort + 临时文件
if exist "%QUEUE_FILE%.sorted" del /f /q "%QUEUE_FILE%.sorted"
sort "%QUEUE_FILE%" /o "%QUEUE_FILE%.sorted"
if exist "%QUEUE_FILE%.sorted" (
    move /y "%QUEUE_FILE%.sorted" "%QUEUE_FILE%" >nul 2>&1
)
exit /b 0

REM ============================================
REM func_push: 将路径添加到队列
REM ============================================
:func_push
set "ADDED_TOTAL=0"
set "ITEM_COUNT=0"

REM 处理从 %2 开始的参数（第一个路径参数）
if not "%~2"=="" goto :push_args
echo 用法: queue.bat push ^<路径1^> [路径2...]
exit /b 1

:push_args
REM 处理多个参数 - 使用循环逐个处理
for %%a in (%~2 %~3 %~4 %~5 %~6 %~7 %~8 %~9) do (
    if not "%%a"=="" (
        if not "%%a"=="push" (
            call :add_single_item "%%a"
        )
    )
)
goto :push_show_result

:add_single_item
set "ITEM=%~1"
if "%ITEM%"=="" exit /b 0
if "%ITEM%"=="push" exit /b 0

REM 检查路径是否包含多个冒号（Windows 路径问题）
for /f "tokens=1* delims=:" %%a in ("%ITEM%") do (
    echo %%b | findstr /r ":" >nul
    if not errorlevel 1 (
        echo 错误: 路径包含多个冒号。Windows 路径应使用 # 分隔符。
        echo 用法: push ^<路径^>#^<起始行^>#^<行数^>
        exit /b 1
    )
)

REM 检查参数是否使用 # 分隔符表示范围：路径#起始行#行数
echo %ITEM% | findstr /c:"#" >nul
if errorlevel 1 (
    REM 未找到 # - 普通路径
    call :add_single_item_normal "%ITEM%"
    exit /b 0
)

REM 包含 # - 尝试解析为范围格式
REM 格式：路径#起始行#行数
for /f "tokens=1,2,3 delims=#" %%a in ("%ITEM%") do (
    set "FILE_PATH=%%a"
    set "START_LINE=%%b"
    set "LINE_COUNT=%%c"
)

REM 校验所有部分是否完整（START_LINE 和 LINE_COUNT 必须非空）
if "%START_LINE%"=="" exit /b 0
if "%LINE_COUNT%"=="" exit /b 0

REM 简单校验：检查 START 和 LINE_COUNT 是否为数字
REM 仅检查它们非空且不包含路径分隔符
if "%START_LINE%"=="" exit /b 0
if "%LINE_COUNT%"=="" exit /b 0

REM 检查文件是否存在
if exist "%FILE_PATH%" (
    for %%f in ("%FILE_PATH%") do echo %%~ff#%START_LINE%#%LINE_COUNT% >> "%CD%\.queue"
    set /a ADDED_TOTAL+=1
) else (
    echo 警告: 路径 %FILE_PATH% 不存在
)
exit /b 0

REM ============================================
REM func_get_rel_path: 从绝对路径获取相对路径
REM 参数: %1 = 路径 (path_to_convert(cd_path))
REM 输出:     REL_PATH 变量（全局）
REM ============================================
:func_get_rel_path
setlocal
set "full_input=%~1"
set "cd_path=%~2"
for /f "tokens=1 delims=(" %%a in ("%full_input%") do set "file_path=%%a"
set "file_path=%file_path:"=%"
set "cd_path_orig=%cd_path:"=%"
set "len_cd=0"
REM 计算 cd_path 的长度
:calc_length
set "cd_path_temp=%cd_path_orig:~0,1%"
if defined cd_path_temp (
    set "cd_path_orig=%cd_path_orig:~1%"
    set /a len_cd+=1
    goto calc_length
)
REM 检查 file_path 是否以 cd_path 开头
set "compare_prefix="
if %len_cd% gtr 0 set "compare_prefix=%file_path:~0,%len_cd%"
if /i "%compare_prefix%"=="%cd_path%" (
    REM 前缀匹配，提取相对部分
    set /a "skip_len=%len_cd%+1"
    set "REL_PATH=%file_path:~%skip_len%"
) else (
    set "REL_PATH="
)
endlocal & set "REL_PATH=%REL_PATH%"
REM 将反斜杠转换为正斜杠以保持一致性
if defined REL_PATH set "REL_PATH=%REL_PATH:\=/%"
exit /b 0

:add_single_item_normal
set "NORMAL_ITEM=%~1"
if "%NORMAL_ITEM%"=="" exit /b 0

REM 检查路径是否存在
if exist "%NORMAL_ITEM%" (
    if exist "%NORMAL_ITEM%\" (
        REM 是目录
        echo 正在扫描目录: %NORMAL_ITEM%
        for /r "%NORMAL_ITEM%" %%f in (*) do (
            echo %%f >> "%CD%\.queue"
        )
    ) else (
        REM 是文件 - 获取绝对路径
        for %%f in ("%NORMAL_ITEM%") do echo %%~ff >> "%CD%\.queue"
        for /f "tokens=1" %%n in ('find /c /v "" ^< "%CD%\.queue"') do set "ADDED_TOTAL=%%n"
    )
) else (
    echo 警告: 路径 %NORMAL_ITEM% 不存在
)
exit /b 0

:push_show_result
REM push 后始终显示队列状态
call :dedup_queue_simple
for /f %%c in ('find /c /v "" ^< "%CD%\.queue"') do set "QUEUE_TOTAL=%%c"
echo ----------------------------------------
echo 队列总数: %QUEUE_TOTAL%
exit /b 0

:dedup_queue_simple
sort "%CD%\.queue" /o "%CD%\.queue"
exit /b 0

:to_abs_path
set "INPUT_PATH=%~1"
if "!INPUT_PATH!"=="" (
    set "%2=%PWD%"
    exit /b 0
)
if "!INPUT_PATH:~0,1!"=="\" (
    set "%2=!INPUT_PATH!"
    exit /b 0
)
if "!INPUT_PATH:~0,1!"=="/" (
    set "%2=!INPUT_PATH!"
    exit /b 0
)
set "%2=%PWD%\!INPUT_PATH!"
exit /b 0

REM ============================================
REM func_status: 查看队列状态
REM ============================================
:func_status
echo 正在检查队列: %CD%
if not exist "%CD%\.queue" (
    echo 提示: 当前目录 %CD% 下未找到队列文件
    exit /b 1
)
for /f %%c in ('find /c /v "" ^< "%CD%\.queue"') do set "COUNT=%%c"
echo 当前工作目录: %CD%
echo 剩余文件数量: %COUNT%
exit /b 0

REM ============================================
REM func_pop: 从队列头部弹出
REM ============================================
:func_pop
if not exist "%CD%\.queue" (
    echo 错误: 队列为空或未初始化
    exit /b 0
)

REM 使用 set /p 读取第一行
set /p ENTRY=<"%CD%\.queue"

REM 检查 ENTRY 是否为空
if "%ENTRY%"=="" (
    echo 队列为空，无法弹出
    exit /b 0
)

REM 检查 ENTRY 是否包含 # 分隔符（范围格式：路径#起始#结束）
echo %ENTRY% | findstr /c:"#" >nul
if errorlevel 1 (
    REM 未找到 # - 普通路径
    echo 下一个审计文件路径: %ENTRY%
    REM 通过检查文件路径是否以当前目录开头来计算相对路径
    setlocal enabledelayedexpansion
    set "file_path=%ENTRY%"
    set "cd_path=%CD%"
    set "rel_path="
    REM 检查 file_path 是否以 cd_path 开头（不区分大小写）
    echo !file_path! | findstr /i /c:"!cd_path!" >nul && (
        REM 通过移除 cd_path 前缀提取相对部分
        for %%d in ("!cd_path!") do set "len=%%~zd"
        set /a "skip=len+1"
        set "rel_path=!file_path:~%skip%!"
        REM 移除可能的盘符（如 D:/）
        for /f "tokens=1* delims=:/" %%i in ("!rel_path!") do (
            if "%%j" neq "" set "rel_path=%%j"
        )
        REM 移除开头的斜杠
        if "!rel_path:~0,1!"=="/" set "rel_path=!rel_path:~1!"
        if "!rel_path:~0,1!"=="\\" set "rel_path=!rel_path:~1!"
    )
    REM 如果未找到相对路径，仅使用文件名
    if "!rel_path!"=="" for %%n in ("%ENTRY%") do set "rel_path=%%~nxn"
    REM 将反斜杠转换为正斜杠
    if defined rel_path set "rel_path=!rel_path:\=/!"
    REM 移除转换后可能引入的开头斜杠
    if "!rel_path:~0,1!"=="/" set "rel_path=!rel_path:~1!"
    echo ^> [提示] 中间报告输出路径为: `^<输出目录^>/!rel_path!-report-0.json`
    endlocal
) else (
    REM 包含 # - 解析为范围格式 - 输出必须在循环内
    for /f "tokens=1,2,3 delims=#" %%a in ("%ENTRY%") do (
        echo 下一个审计文件路径: %%a

        REM 计算相对路径和结束行
        setlocal enabledelayedexpansion
        set "start_line=%%b"
        set "line_count=%%c"
        set /a "end_line=start_line + line_count - 1"
        if !end_line! lss !start_line! set "end_line=!start_line!"
        echo ^> [Diff 范围] 起始行: !start_line!, 结束行: !end_line!

        set "file_path=%%a"
        set "cd_path=%CD%"
        set "rel_path="
        REM 检查 file_path 是否以 cd_path 开头（不区分大小写）
        echo !file_path! | findstr /i /c:"!cd_path!" >nul && (
            REM 通过移除 cd_path 前缀提取相对部分
            for %%d in ("!cd_path!") do set "len=%%~zd"
            set /a "skip=len+1"
            set "rel_path=!file_path:~%skip%!"
            REM 移除可能的盘符（如 D:/）
            for /f "tokens=1* delims=:/" %%i in ("!rel_path!") do (
                if "%%j" neq "" set "rel_path=%%j"
            )
            REM 移除开头的斜杠
            if "!rel_path:~0,1!"=="/" set "rel_path=!rel_path:~1!"
            if "!rel_path:~0,1!"=="\\" set "rel_path=!rel_path:~1!"
        )
        REM 如果未找到相对路径，仅使用文件名
        if "!rel_path!"=="" for %%n in ("%%a") do set "rel_path=%%~nxn"
        REM 将反斜杠转换为正斜杠
        if defined rel_path set "rel_path=!rel_path:\=/!"
        REM 移除转换后可能引入的开头斜杠
        if "!rel_path:~0,1!"=="/" set "rel_path=!rel_path:~1!"
        echo ^> [提示] 中间报告输出路径为: `^<输出目录^>/!rel_path!-L!start_line!-report-0.json`
        endlocal
    )
)

REM 删除第一行
setlocal enabledelayedexpansion
set "file=%CD%\.queue"
set "temp=%CD%\.queue.tmp"
(
    for /f "skip=1 delims=" %%i in ('type "!file!"') do (
        set "line=%%i"
        if not "!line!"=="" echo !line!
    )
) > "!temp!"
endlocal
move /y "%CD%\.queue.tmp" "%CD%\.queue" >nul 2>&1

exit /b 0

REM ============================================
REM compute_rel_path: 计算 file_path 相对于 cd_path 的相对路径
REM 使用全局变量: file_path, cd_path
REM 设置: rel_path
REM ============================================
:compute_rel_path
set "rel_path="

REM 使用简单的子串提取方法
REM 检查 file_path 是否长于 cd_path 且可能以其开头
REM 使用逐字符比较

set "fp=!file_path!"
set "cp=!cd_path!"
set "idx=0"

REM 查找路径分叉点
:compare_chars
set "fc=!fp:~%idx%,1%"
set "fc1=!fc!"
if "!fc1:~0,1!"=="/" set "fc=\"
if "!fc1:~0,1!"=="/" set "fc=\"

set "cc=!cp:~%idx%,1%"
if not defined cc goto :paths_match
if not defined fc goto :no_match
if /i not "!fc!"=="!cc!" goto :no_match
set /a idx+=1
goto compare_chars

:paths_match
REM 路径匹配到 cd_path 长度，提取剩余部分
for %%d in ("!cp!") do set "len=0"
:count_len
set "c=!cp:~%len%,1%"
if defined c (
    set /a len+=1
    goto count_len
)
set /a skip=len+1
set "rel_path=!fp:~%skip%!"
exit /b 0

:no_match
REM 路径不匹配，仅使用文件名
for %%f in ("!file_path!") do set "rel_path=%%~nxf"
exit /b 0

REM ============================================
REM func_diff: 将 git diff 结果加入队列
REM ============================================
:func_diff
setlocal enabledelayedexpansion

REM 1. 检查 git 环境
git rev-parse --is-inside-work-tree >nul 2>&1
if !errorlevel! neq 0 (
    echo 错误: 当前目录不在 git 仓库中
    exit /b 1
)

REM 2. 获取 Git 根目录
for /f "delims=" %%g in ('git rev-parse --show-toplevel') do set "GIT_ROOT=%%g"

REM 3. 执行 git diff
REM 使用 shift 获取 "diff" 之后的所有参数
set "DIFF_ARGS="
:get_diff_args
shift
if "%~1"=="" goto :diff_args_done
set "DIFF_ARGS=!DIFF_ARGS! %~1"
goto :get_diff_args
:diff_args_done

REM 清空队列
type nul > "%QUEUE_FILE%"

REM 4. 解析 git diff 输出
set "COUNT=0"
set "CURRENT_FILE="

REM 执行 git diff 并逐行捕获输出
set "DIFF_TEMP_FILE=%TEMP%\queue_diff_%RANDOM%.txt"
git diff --unified=0 %DIFF_ARGS% > "%DIFF_TEMP_FILE%" 2>&1

if not exist "%DIFF_TEMP_FILE%" (
    echo [错误] 创建 diff 输出文件失败。
    goto :end
)

REM 从临时文件读取
for /f "usebackq delims=" %%d in ("%DIFF_TEMP_FILE%") do (
    set "LINE=%%d"

    REM --- A. 解析文件路径 ---
    REM 检查行是否以 "+++ b/" 或 "+++ " 开头
    if "!LINE:~0,4!"=="+++ " (
        for /f "tokens=2 delims= " %%p in ("!LINE!") do set "REL_PATH=%%p"

        REM 移除 "b/" 前缀（如果存在）
        if "!REL_PATH:~0,2!"=="b/" set "REL_PATH=!REL_PATH:~2!"
        if "!REL_PATH:~0,2!"=="b\\" set "REL_PATH=!REL_PATH:~2!"

        if "!REL_PATH!"=="/dev/null" (
            set "CURRENT_FILE="
        ) else (
            set "CURRENT_FILE=%GIT_ROOT%\!REL_PATH!"
            if not exist "!CURRENT_FILE!" (
                set "CURRENT_FILE="
            )
        )
    )

    REM --- B. 解析行范围 ---
    REM 检查行是否以 "@@ " 开头
    if defined CURRENT_FILE (
        if "!LINE:~0,3!"=="@@ " (
            REM 提取 @@ 之间的范围部分
            for /f "tokens=1 delims=@" %%r in ("!LINE!") do set "RANGE=%%r"

            REM 提取 + 号之后的部分
            for /f "tokens=2 delims=+" %%s in ("!RANGE!") do set "START=%%s"

            REM 提取起始行号（逗号之前）
            for /f "tokens=1 delims=," %%t in ("!START!") do set "START_LINE=%%t"

            REM 去除 START_LINE 前导空格
            for /f "tokens=* delims= " %%t in ("!START_LINE!") do set "START_LINE=%%t"

            REM 去除尾部空格（使用 for 循环提取第一个 token）
            for %%t in (!START_LINE!) do set "START_LINE=%%t"

            REM 默认行数为 1
            set "LINE_COUNT=1"

            REM 检查是否有逗号（表示行数）
            if not "!START!"=="!START:,=!" (
                for /f "tokens=2 delims=," %%u in ("!START!") do set "LINE_COUNT=%%u"
            )

            REM 写入队列：绝对路径#起始行#行数
            echo !CURRENT_FILE!#!START_LINE!#!LINE_COUNT! >> "%QUEUE_FILE%"
            set /a COUNT+=1
        )
    )
)

if exist "%DIFF_TEMP_FILE%" del "%DIFF_TEMP_FILE%"

echo ----------------------------------------
echo 已写入 %COUNT% 个变更块到队列
echo 队列文件: %QUEUE_FILE%
echo ----------------------------------------
goto :end

REM ============================================
REM func_clean: 清理队列
REM ============================================
:func_clean
if not exist "%CD%\.queue" (
    echo 错误: 当前目录 %CD% 下队列为空或未初始化
    exit /b 1
)
for /f %%c in ('find /c /v "" ^< "%CD%\.queue"') do set COUNT=%%c
if %COUNT%==0 (
    del /f /q "%CD%\.queue" 2>nul
    if not exist "%CD%\.queue" (
        echo 队列清理完成
    ) else (
        echo 清理失败
    )
    exit /b 0
)
echo 剩余待审计文件数量: %COUNT%，无法清理
exit /b 0

:end
