package tech.easyflow.ai.mapper;

import com.mybatisflex.core.BaseMapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import tech.easyflow.ai.entity.DocumentImportBatchItem;

import java.math.BigInteger;
import java.util.Date;

/**
 * 文档批量导入文件项映射层。
 *
 * @author Codex
 * @since 2026-07-31
 */
public interface DocumentImportBatchItemMapper extends BaseMapper<DocumentImportBatchItem> {

    /**
     * 锁定运行批次中的失败项并取得本轮恢复权。
     *
     * <p>同时锁定批次和文件项，使恢复任务创建与批次熔断串行化。</p>
     *
     * @param itemId 文件项 ID
     * @return 可恢复文件项；状态已变化时返回 null
     */
    @Select("SELECT item.* FROM tb_document_import_batch_item item "
        + "INNER JOIN tb_document_import_batch batch ON batch.id=item.batch_id "
        + "WHERE item.id=#{itemId} AND item.status='FAILED' "
        + "AND batch.status='RUNNING' FOR UPDATE")
    DocumentImportBatchItem selectFailedForRetry(
        @Param("itemId") BigInteger itemId
    );

    /**
     * 将中断批次中尚未结束的文件项统一收口为可恢复失败。
     *
     * @param batchId 批次 ID
     * @param errorSummary 用户可见错误摘要
     * @param failureCode 稳定失败码
     * @param modified 修改时间
     * @return 更新文件项数量
     */
    @Update("UPDATE tb_document_import_batch_item item "
        + "INNER JOIN tb_document_import_batch batch ON batch.id=item.batch_id "
        + "SET item.status='FAILED', item.error_summary=#{errorSummary}, "
        + "item.failure_code=#{failureCode}, item.retryable=1, "
        + "item.modified=#{modified} "
        + "WHERE item.batch_id=#{batchId} AND batch.status='INTERRUPTED' "
        + "AND item.status IN ('PENDING','RUNNING','UPLOADED')")
    int interruptActiveItems(
        @Param("batchId") BigInteger batchId,
        @Param("errorSummary") String errorSummary,
        @Param("failureCode") String failureCode,
        @Param("modified") Date modified
    );

    /**
     * 原子领取文件上传权并刷新批次进度时间。
     *
     * <p>文件项与批次在同一条 MySQL 多表更新中加锁，确保上传领取和
     * 超时取消之间不存在旧快照窗口。</p>
     *
     * @param batchId 批次 ID
     * @param itemId 文件项 ID
     * @param knowledgeId 知识库 ID
     * @param modified 修改时间
     * @return 更新行数
     */
    @Update("UPDATE tb_document_import_batch batch "
        + "INNER JOIN tb_document_import_batch_item item "
        + "ON item.batch_id=batch.id SET "
        + "item.stage='UPLOAD', item.status='UPLOADING', "
        + "item.error_summary=NULL, item.failure_code=NULL, "
        + "item.retryable=0, item.modified=#{modified}, "
        + "batch.modified=#{modified}, batch.version=batch.version + 1 "
        + "WHERE batch.id=#{batchId} AND batch.knowledge_id=#{knowledgeId} "
        + "AND batch.status IN ('UPLOADING','READY') "
        + "AND item.id=#{itemId} AND item.status='PENDING' "
        + "AND item.cleanup_pending=0 AND item.storage_locator IS NULL")
    int claimUpload(
        @Param("batchId") BigInteger batchId,
        @Param("itemId") BigInteger itemId,
        @Param("knowledgeId") BigInteger knowledgeId,
        @Param("modified") Date modified
    );

    /**
     * 按预期状态原子迁移文件项。
     *
     * @param id 文件项 ID
     * @param expectedStatus 预期状态
     * @param stage 新阶段
     * @param status 新状态
     * @param errorSummary 错误摘要
     * @param failureCode 稳定失败码
     * @param retryable 是否允许批量重试
     * @param attemptDelta 重试次数增量
     * @param modified 修改时间
     * @return 更新行数
     */
    @Update("UPDATE tb_document_import_batch_item item "
        + "INNER JOIN tb_document_import_batch batch ON batch.id=item.batch_id SET "
        + "item.stage=#{stage}, item.status=#{status}, "
        + "item.error_summary=#{errorSummary}, item.failure_code=#{failureCode}, "
        + "item.retryable=#{retryable}, "
        + "item.attempt_count=item.attempt_count + #{attemptDelta}, "
        + "item.modified=#{modified} WHERE item.id=#{id} "
        + "AND item.status=#{expectedStatus} AND batch.status<>'INTERRUPTED'")
    int transitionStatus(
        @Param("id") BigInteger id,
        @Param("expectedStatus") String expectedStatus,
        @Param("stage") String stage,
        @Param("status") String status,
        @Param("errorSummary") String errorSummary,
        @Param("failureCode") String failureCode,
        @Param("retryable") boolean retryable,
        @Param("attemptDelta") int attemptDelta,
        @Param("modified") Date modified
    );

    /**
     * 将上传项原子绑定到创建后的文档。
     *
     * @param id 文件项 ID
     * @param documentId 文档 ID
     * @param modified 修改时间
     * @return 更新行数
     */
    @Update("UPDATE tb_document_import_batch_item SET "
        + "document_id=#{documentId}, stage='PARSE', status='PENDING', "
        + "error_summary=NULL, retryable=0, modified=#{modified} "
        + "WHERE id=#{id} AND status='UPLOADED' AND document_id IS NULL")
    int bindDocument(
        @Param("id") BigInteger id,
        @Param("documentId") BigInteger documentId,
        @Param("modified") Date modified
    );

    /**
     * 将缺少文档的失败项原子绑定到恢复创建的文档。
     *
     * @param id 文件项 ID
     * @param documentId 恢复创建的文档 ID
     * @param modified 修改时间
     * @return 文件项仍属于运行批次且绑定成功时返回 1
     */
    @Update("UPDATE tb_document_import_batch_item item "
        + "INNER JOIN tb_document_import_batch batch ON batch.id=item.batch_id SET "
        + "item.document_id=#{documentId}, item.stage='PARSE', "
        + "item.status='PENDING', item.error_summary=NULL, "
        + "item.failure_code=NULL, item.retryable=0, "
        + "item.attempt_count=COALESCE(item.attempt_count, 0) + 1, "
        + "item.modified=#{modified} "
        + "WHERE item.id=#{id} AND item.status='FAILED' "
        + "AND item.document_id IS NULL AND batch.status='RUNNING'")
    int bindFailedDocument(
        @Param("id") BigInteger id,
        @Param("documentId") BigInteger documentId,
        @Param("modified") Date modified
    );

    /**
     * 更新恢复失败项的业务错误，但不提前汇总批次终态。
     *
     * @param id 文件项 ID
     * @param batchId 批次 ID
     * @param errorSummary 用户可见错误摘要
     * @param modified 修改时间
     * @return 文件项仍属于运行批次且更新成功时返回 1
     */
    @Update("UPDATE tb_document_import_batch_item item "
        + "INNER JOIN tb_document_import_batch batch ON batch.id=item.batch_id SET "
        + "item.error_summary=#{errorSummary}, item.failure_code=NULL, "
        + "item.retryable=1, item.modified=#{modified} "
        + "WHERE item.id=#{id} AND item.batch_id=#{batchId} "
        + "AND item.status='FAILED' AND batch.status='RUNNING'")
    int updateFailedRetryError(
        @Param("id") BigInteger id,
        @Param("batchId") BigInteger batchId,
        @Param("errorSummary") String errorSummary,
        @Param("modified") Date modified
    );

    /**
     * 在物理写入前持久化可恢复存储定位符。
     *
     * @param id 文件项 ID
     * @param storageLocator 可恢复存储定位符
     * @param modified 修改时间
     * @return 更新行数
     */
    @Update("UPDATE tb_document_import_batch_item SET "
        + "storage_locator=#{storageLocator}, cleanup_pending=0, "
        + "modified=#{modified} "
        + "WHERE id=#{id} AND status='UPLOADING' "
        + "AND storage_locator IS NULL AND cleanup_pending=0")
    int registerUploadWriteIntent(
        @Param("id") BigInteger id,
        @Param("storageLocator") String storageLocator,
        @Param("modified") Date modified
    );

    /**
     * 在物理写入前原子撤销上传领取与写意图登记。
     *
     * <p>同时兼容登记未提交和提交结果未知两种情况；仅清除空定位符或
     * 本次预期定位符，避免覆盖其他请求的新写入意图。</p>
     *
     * @param id 文件项 ID
     * @param storageLocator 本次预期存储定位符，可为空
     * @param errorSummary 上传准备失败摘要
     * @param modified 修改时间
     * @return 更新行数
     */
    @Update("UPDATE tb_document_import_batch batch "
        + "INNER JOIN tb_document_import_batch_item item "
        + "ON item.batch_id=batch.id SET "
        + "item.status='PENDING', item.storage_locator=NULL, "
        + "item.file_path=NULL, item.cleanup_pending=0, "
        + "item.error_summary=#{errorSummary}, item.failure_code=NULL, "
        + "item.retryable=0, item.modified=#{modified}, "
        + "batch.modified=#{modified}, batch.version=batch.version + 1 "
        + "WHERE item.id=#{id} AND item.status='UPLOADING' "
        + "AND item.cleanup_pending=0 "
        + "AND (item.storage_locator IS NULL "
        + "OR item.storage_locator=#{storageLocator})")
    int abortUploadBeforeWrite(
        @Param("id") BigInteger id,
        @Param("storageLocator") String storageLocator,
        @Param("errorSummary") String errorSummary,
        @Param("modified") Date modified
    );

    /**
     * 在文件仍处于上传中时原子绑定存储路径并完成上传。
     *
     * @param id 文件项 ID
     * @param filePath 存储路径
     * @param storageLocator 可恢复存储定位符
     * @param modified 修改时间
     * @return 更新行数
     */
    @Update("UPDATE tb_document_import_batch_item SET "
        + "file_path=#{filePath}, status='UPLOADED', error_summary=NULL, "
        + "cleanup_pending=0, retryable=0, modified=#{modified} "
        + "WHERE id=#{id} AND status='UPLOADING' "
        + "AND storage_locator=#{storageLocator}")
    int completeUpload(
        @Param("id") BigInteger id,
        @Param("filePath") String filePath,
        @Param("storageLocator") String storageLocator,
        @Param("modified") Date modified
    );

    /**
     * 将指定可恢复写意图标记为等待清理。
     *
     * @param id 文件项 ID
     * @param storageLocator 可恢复存储定位符
     * @param modified 修改时间
     * @return 更新行数
     */
    @Update("UPDATE tb_document_import_batch_item SET "
        + "cleanup_pending=1, modified=#{modified} "
        + "WHERE id=#{id} AND status IN ('UPLOADING','CANCELLED') "
        + "AND storage_locator=#{storageLocator}")
    int markUploadCleanupPending(
        @Param("id") BigInteger id,
        @Param("storageLocator") String storageLocator,
        @Param("modified") Date modified
    );

    /**
     * 将已取消文件项的现有存储引用标记为等待清理。
     *
     * @param id 文件项 ID
     * @param modified 修改时间
     * @return 更新行数
     */
    @Update("UPDATE tb_document_import_batch_item SET "
        + "cleanup_pending=1, modified=#{modified} "
        + "WHERE id=#{id} AND status='CANCELLED' "
        + "AND (storage_locator IS NOT NULL OR file_path IS NOT NULL)")
    int markCancelledCleanupPending(
        @Param("id") BigInteger id,
        @Param("modified") Date modified
    );

    /**
     * 为新导入项记录待覆盖的历史文档。
     *
     * @param id 文件项 ID
     * @param replacedDocumentId 历史文档 ID
     * @param modified 修改时间
     * @return 更新行数
     */
    @Update("UPDATE tb_document_import_batch_item SET "
        + "replaced_document_id=#{replacedDocumentId}, modified=#{modified} "
        + "WHERE id=#{id} AND status='UPLOADED' AND replaced_document_id IS NULL")
    int markReplacement(
        @Param("id") BigInteger id,
        @Param("replacedDocumentId") BigInteger replacedDocumentId,
        @Param("modified") Date modified
    );

    /**
     * 完成历史文档清理后原子清除覆盖标记。
     *
     * @param id 文件项 ID
     * @param replacedDocumentId 历史文档 ID
     * @param modified 修改时间
     * @return 更新行数
     */
    @Update("UPDATE tb_document_import_batch_item SET "
        + "replaced_document_id=NULL, modified=#{modified} "
        + "WHERE id=#{id} AND replaced_document_id=#{replacedDocumentId}")
    int clearReplacement(
        @Param("id") BigInteger id,
        @Param("replacedDocumentId") BigInteger replacedDocumentId,
        @Param("modified") Date modified
    );

    /**
     * 对象删除成功后原子清理上传中项并恢复待上传状态。
     *
     * @param id 文件项 ID
     * @param storageLocator 已删除对象的恢复定位符
     * @param filePath 已删除对象的兼容存储路径
     * @param recoveryMessage 恢复为待上传时的提示
     * @param modified 修改时间
     * @return 更新行数
     */
    @Update("UPDATE tb_document_import_batch batch "
        + "INNER JOIN tb_document_import_batch_item item "
        + "ON item.batch_id=batch.id SET "
        + "item.stage='UPLOAD', item.error_summary=#{recoveryMessage}, "
        + "item.failure_code=NULL, item.retryable=0, item.status='PENDING', "
        + "item.cleanup_pending=0, item.storage_locator=NULL, "
        + "item.file_path=NULL, item.modified=#{modified}, "
        + "batch.modified=#{modified}, batch.version=batch.version + 1 "
        + "WHERE item.id=#{id} AND item.cleanup_pending=1 "
        + "AND item.status='UPLOADING' "
        + "AND item.storage_locator <=> #{storageLocator} "
        + "AND item.file_path <=> #{filePath}")
    int completeUploadingStorageCleanup(
        @Param("id") BigInteger id,
        @Param("storageLocator") String storageLocator,
        @Param("filePath") String filePath,
        @Param("recoveryMessage") String recoveryMessage,
        @Param("modified") Date modified
    );

    /**
     * 对象删除成功后原子清理已取消项并保持取消状态。
     *
     * @param id 文件项 ID
     * @param storageLocator 已删除对象的恢复定位符
     * @param filePath 已删除对象的兼容存储路径
     * @param modified 修改时间
     * @return 更新行数
     */
    @Update("UPDATE tb_document_import_batch batch "
        + "INNER JOIN tb_document_import_batch_item item "
        + "ON item.batch_id=batch.id SET "
        + "item.cleanup_pending=0, item.storage_locator=NULL, "
        + "item.file_path=NULL, item.modified=#{modified}, "
        + "batch.modified=#{modified}, batch.version=batch.version + 1 "
        + "WHERE item.id=#{id} AND item.cleanup_pending=1 "
        + "AND item.status='CANCELLED' "
        + "AND item.storage_locator <=> #{storageLocator} "
        + "AND item.file_path <=> #{filePath}")
    int completeCancelledStorageCleanup(
        @Param("id") BigInteger id,
        @Param("storageLocator") String storageLocator,
        @Param("filePath") String filePath,
        @Param("modified") Date modified
    );
}
