package com.gzzm.lobster.runtime;

import com.gzzm.lobster.llm.CancelReason;

/**
 * RunEvent —— AgentRuntime 主循环事件 / Lifecycle events from the agent main loop.
 *
 * <p>设计意图（与 Claude Code 对齐）：
 * <ul>
 *   <li><b>只做进程内分发</b>：通过 {@link RunEventBus} 推给当前 SSE emitter / 进程内订阅者</li>
 *   <li><b>绝不持久化</b>：delta 级事件写 DB 会把 I/O 放大到不可用；transcript 已是事件日志投影</li>
 *   <li><b>只承载数据</b>：没有 id / 时间戳字段（需要时订阅者自行打）</li>
 * </ul>
 *
 * <p>Java 8 没有 sealed interface —— 用 abstract base + 静态嵌套 final 子类表达封闭类型；
 * 消费方按 {@code instanceof} 分支。
 */
public abstract class RunEvent {

    public enum Kind {
        RUN_STARTED, TURN_STARTED,
        LLM_STARTED, LLM_DELTA, LLM_COMPLETED,
        TOOL_CALL_STARTED, TOOL_CALL_COMPLETED,
        PENDING_CREATED,
        TURN_ENDED, RUN_ENDED, RUN_CANCELLED, RUN_ERROR
    }

    public final String runId;
    public final String threadId;
    private final Kind kind;

    protected RunEvent(Kind kind, String runId, String threadId) {
        this.kind = kind;
        this.runId = runId;
        this.threadId = threadId;
    }

    public final Kind getKind() { return kind; }

    // ---- 子类 ----

    public static final class RunStarted extends RunEvent {
        public final String modelId;
        public RunStarted(String runId, String threadId, String modelId) {
            super(Kind.RUN_STARTED, runId, threadId);
            this.modelId = modelId;
        }
    }

    public static final class TurnStarted extends RunEvent {
        public final int turn;
        public TurnStarted(String runId, String threadId, int turn) {
            super(Kind.TURN_STARTED, runId, threadId);
            this.turn = turn;
        }
    }

    public static final class LlmStarted extends RunEvent {
        public final String modelId;
        public LlmStarted(String runId, String threadId, String modelId) {
            super(Kind.LLM_STARTED, runId, threadId);
            this.modelId = modelId;
        }
    }

    public static final class LlmDelta extends RunEvent {
        public final String delta;
        public LlmDelta(String runId, String threadId, String delta) {
            super(Kind.LLM_DELTA, runId, threadId);
            this.delta = delta;
        }
    }

    public static final class LlmCompleted extends RunEvent {
        public final String finishReason;
        public final int promptTokens;
        public final int completionTokens;
        public LlmCompleted(String runId, String threadId, String finishReason,
                            int promptTokens, int completionTokens) {
            super(Kind.LLM_COMPLETED, runId, threadId);
            this.finishReason = finishReason;
            this.promptTokens = promptTokens;
            this.completionTokens = completionTokens;
        }
    }

    public static final class ToolCallStarted extends RunEvent {
        public final String toolCallId;
        public final String toolName;
        public final String argumentsJson;
        public ToolCallStarted(String runId, String threadId, String toolCallId,
                               String toolName, String argumentsJson) {
            super(Kind.TOOL_CALL_STARTED, runId, threadId);
            this.toolCallId = toolCallId;
            this.toolName = toolName;
            this.argumentsJson = argumentsJson;
        }
    }

    public static final class ToolCallCompleted extends RunEvent {
        public final String toolCallId;
        public final String toolName;
        public final String status;       // ok / error / pending
        public final String summary;
        public final long   durationMs;
        public ToolCallCompleted(String runId, String threadId, String toolCallId,
                                 String toolName, String status, String summary, long durationMs) {
            super(Kind.TOOL_CALL_COMPLETED, runId, threadId);
            this.toolCallId = toolCallId;
            this.toolName = toolName;
            this.status = status;
            this.summary = summary;
            this.durationMs = durationMs;
        }
    }

    public static final class PendingCreated extends RunEvent {
        public final String pendingRequestId;
        public final String pendingType;
        public final String title;
        public final String toolName;
        public PendingCreated(String runId, String threadId, String pendingRequestId,
                              String pendingType, String title, String toolName) {
            super(Kind.PENDING_CREATED, runId, threadId);
            this.pendingRequestId = pendingRequestId;
            this.pendingType = pendingType;
            this.title = title;
            this.toolName = toolName;
        }
    }

    public static final class TurnEnded extends RunEvent {
        public final int turn;
        public final String hint;     // 如 "no_progress" / "loop_detected" / null
        public TurnEnded(String runId, String threadId, int turn, String hint) {
            super(Kind.TURN_ENDED, runId, threadId);
            this.turn = turn;
            this.hint = hint;
        }
    }

    public static final class RunEnded extends RunEvent {
        public final String exitReason;
        public RunEnded(String runId, String threadId, String exitReason) {
            super(Kind.RUN_ENDED, runId, threadId);
            this.exitReason = exitReason;
        }
    }

    public static final class RunCancelled extends RunEvent {
        public final CancelReason reason;
        public RunCancelled(String runId, String threadId, CancelReason reason) {
            super(Kind.RUN_CANCELLED, runId, threadId);
            this.reason = reason;
        }
    }

    public static final class RunError extends RunEvent {
        public final String code;
        public final String message;
        public final Throwable cause;   // 可为 null
        public RunError(String runId, String threadId, String code, String message, Throwable cause) {
            super(Kind.RUN_ERROR, runId, threadId);
            this.code = code;
            this.message = message;
            this.cause = cause;
        }
    }
}
