/**
 * Copyright (c) 2025-2026, Michael Yang 杨福海 (fuhai999@gmail.com).
 * <p>
 * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.gnu.org/licenses/lgpl-3.0.txt
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.easyagents.flow.core.chain.runtime;

import java.util.List;

public interface TriggerStore {

    Trigger save(Trigger trigger);

    /**
     * 仅在同 ID 触发器尚不存在时原子保存。
     *
     * <p>缺省实现保证同一仓储实例内原子；分布式仓储必须覆盖为跨进程原子操作。</p>
     *
     * @param trigger 带稳定 ID 的触发器
     * @return 本次成功创建时为 {@code true}，已存在时为 {@code false}
     */
    default boolean saveIfAbsent(Trigger trigger) {
        synchronized (this) {
            if (find(trigger.getId()) != null) {
                return false;
            }
            save(trigger);
            return true;
        }
    }

    boolean remove(String triggerId);

    Trigger find(String triggerId);

    List<Trigger> findDue(long uptoTimestamp);

    List<Trigger> findAllPending();

    /**
     * 原子认领待执行触发器。
     * <p>
     * 缺省实现适用于单进程仓储：先读取再以删除结果作为认领成功标志。
     *
     * @param triggerId 触发器 ID
     * @param leaseMillis 认领租约毫秒数
     * @return 认领成功时返回触发器，否则返回 null
     */
    default Trigger claim(String triggerId, long leaseMillis) {
        Trigger trigger = find(triggerId);
        return trigger != null && remove(triggerId) ? trigger : null;
    }

    /**
     * 原子认领已加载的待执行触发器。
     *
     * <p>分布式仓储可使用候选触发器中的实例 ID 构建与本次 claim 绑定的执行守卫，
     * 避免认领前额外读取完整触发器负载。</p>
     *
     * @param candidate 扫描或主动触发阶段已加载的候选触发器
     * @param leaseMillis 认领租约毫秒数
     * @return 认领成功时返回触发器，否则返回 {@code null}
     */
    default Trigger claim(Trigger candidate, long leaseMillis) {
        return candidate == null ? null : claim(candidate.getId(), leaseMillis);
    }

    /**
     * 仅按 ID 续期触发器租约。
     *
     * <p>分布式仓储无法仅凭 ID 验证 owner token。该兼容入口不应再用于运行时提交路径，
     * 调用方必须保留 {@link Trigger} 认领对象并使用
     * {@link #renewClaim(Trigger, long)}。</p>
     *
     * @param triggerId 触发器 ID
     * @param leaseMillis 新租约毫秒数
     * @return 不适用
     * @throws UnsupportedOperationException 始终抛出，防止无 owner token 的不安全续期
     */
    @Deprecated
    default boolean renewClaim(String triggerId, long leaseMillis) {
        throw new UnsupportedOperationException("Trigger claim token is required");
    }

    /**
     * 续期调用方持有的具体触发器租约。
     *
     * @param trigger 已认领触发器对象
     * @param leaseMillis 新租约毫秒数
     * @return 续期成功时为 true
     */
    default boolean renewClaim(Trigger trigger, long leaseMillis) {
        // 单进程缺省仓储在 claim 时已经移除触发器，不需要租约续期。
        return true;
    }

    /**
     * 仅按 ID 确认触发器。
     *
     * <p>分布式仓储无法仅凭 ID 验证 owner token，运行时必须使用
     * {@link #acknowledge(Trigger)}。</p>
     *
     * @param triggerId 触发器 ID
     * @throws UnsupportedOperationException 始终抛出，防止旧 owner 删除新 owner 的任务
     */
    @Deprecated
    default void acknowledge(String triggerId) {
        throw new UnsupportedOperationException("Claimed trigger object is required");
    }

    /**
     * 确认调用方持有的具体触发器执行成功。
     *
     * @param trigger 已认领触发器对象
     */
    default void acknowledge(Trigger trigger) {
        // 单进程缺省认领已经移除触发器，无需再次处理。
    }

    /**
     * 释放失败执行的触发器，使其可以再次被认领。
     *
     * @param trigger 执行失败的触发器
     */
    default void release(Trigger trigger) {
        save(trigger);
    }

    /**
     * 在保持当前 claim 的同时持久化待补写死信标记。
     *
     * <p>分布式仓储必须校验具体 owner token；进程崩溃后，新 owner 依靠该标记
     * 跳过业务执行并继续终态协议。</p>
     *
     * @param trigger 已认领且标记为待补写死信的触发器
     */
    default void markDeadLetterPending(
            Trigger trigger) {
        // 单进程仓储的 claimed trigger 仅存在于当前调用栈，无需额外持久化。
    }

    /**
     * 将不可继续执行或超过投递上限的触发器移入死信。
     *
     * @param trigger 已认领触发器
     * @param reason 死信原因
     */
    default void deadLetter(Trigger trigger, String reason) {
        acknowledge(trigger);
    }
}
