package com.gzzm.lobster.pending;

import com.gzzm.lobster.common.IdGenerator;
import com.gzzm.lobster.common.JsonUtil;
import com.gzzm.lobster.common.LobsterException;
import com.gzzm.lobster.common.PendingRequestStatus;
import com.gzzm.lobster.common.PendingRequestType;
import com.gzzm.lobster.identity.UserContext;
import com.gzzm.lobster.thread.ThreadRoom;
import com.gzzm.platform.commons.Tools;
import net.cyan.arachne.annotation.Service;
import net.cyan.nest.annotation.Inject;

import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * PendingRequestService —— PendingRequest 领域服务 /
 * Domain service for pending requests.
 */
@Service
public class PendingRequestService {

    @Inject
    private PendingRequestDao pendingDao;

    /** thunwind DAO 跨线程保护 —— 详见 feedback_thunwind_dao_thread_binding */
    private PendingRequestDao pendingDao() {
        try {
            PendingRequestDao d = Tools.getBean(PendingRequestDao.class);
            if (d != null) return d;
        } catch (Throwable ignore) { /* fallback */ }
        return pendingDao;
    }

    public PendingRequest create(ThreadRoom thread, UserContext user, PendingRequestType type,
                                 String title, String prompt, Map<String, Object> payload,
                                 List<String> allowedActions, String relatedArtifactId,
                                 String sourceRunId, String toolCallId) throws Exception {
        PendingRequest p = new PendingRequest();
        p.setRequestId(IdGenerator.pendingId());
        p.setThreadId(thread.getThreadId());
        p.setUserId(user.getUserId());
        p.setType(type);
        p.setTitle(title);
        p.setPrompt(prompt);
        p.setPayloadJson(payload == null ? null : JsonUtil.toJson(payload));
        p.setAllowedActionsJson(allowedActions == null ? null : JsonUtil.toJson(allowedActions));
        p.setRelatedArtifactId(relatedArtifactId);
        p.setSourceRunId(sourceRunId);
        p.setToolCallId(toolCallId);
        p.setStatus(PendingRequestStatus.open);
        p.setCreateTime(new Date());
        pendingDao().save(p);
        return p;
    }

    public PendingRequest get(String requestId) throws Exception {
        return pendingDao().getRequest(requestId);
    }

    public List<PendingRequest> listOpenByThread(String threadId) throws Exception {
        return pendingDao().listOpenByThread(threadId, PendingRequestStatus.open);
    }

    public boolean threadHasOpen(String threadId) throws Exception {
        Long c = pendingDao().countOpenByThread(threadId, PendingRequestStatus.open);
        return c != null && c > 0;
    }

    /**
     * 用户响应 pending request，并返回转化出的 thread 新输入内容。
     * Resolve a pending request and return the synthesized new user input.
     *
     * <p>Caller should then create a new RunRequest using this input.
     */
    public ResolveResult resolve(String requestId, UserContext user, String action,
                                 Map<String, Object> responsePayload) throws Exception {
        PendingRequest p = pendingDao().getRequest(requestId);
        if (p == null) throw new LobsterException("pending.not_found", "Request not found");
        if (!p.getUserId().equals(user.getUserId())) {
            throw new LobsterException("pending.forbidden", "Pending request not owned by current user");
        }
        if (p.getStatus() != PendingRequestStatus.open) {
            throw new LobsterException("pending.state", "Pending request already " + p.getStatus());
        }
        int updated = pendingDao().resolve(PendingRequestStatus.resolved,
                responsePayload == null ? "{}" : JsonUtil.toJson(responsePayload),
                new Date(), requestId, PendingRequestStatus.open);
        if (updated <= 0) {
            PendingRequest latest = pendingDao().getRequest(requestId);
            throw new LobsterException("pending.state", "Pending request already "
                    + (latest == null ? "changed" : latest.getStatus()));
        }
        String userInput = synthesizeInput(p, action, responsePayload);
        return new ResolveResult(p, userInput);
    }

    public void reopen(String requestId) {
        if (requestId == null || requestId.isEmpty()) return;
        try {
            pendingDao().reopen(PendingRequestStatus.open, requestId, PendingRequestStatus.resolved);
        } catch (Throwable t) {
            try { Tools.log("[PendingRequestService] reopen failed: " + requestId, t); } catch (Throwable ignore) { /* ignore */ }
        }
    }

    public int cancelOpenBySourceRun(String sourceRunId) throws Exception {
        if (sourceRunId == null || sourceRunId.isEmpty()) return 0;
        return pendingDao().cancelOpenBySourceRun(PendingRequestStatus.cancelled, new Date(),
                sourceRunId, PendingRequestStatus.open);
    }

    private String synthesizeInput(PendingRequest p, String action, Map<String, Object> payload) {
        if (p.getType() == PendingRequestType.confirm_action) {
            return "[用户对「" + p.getTitle() + "」的回应] action=" + action
                    + (payload == null || payload.isEmpty() ? "" : " payload=" + JsonUtil.toJson(payload));
        }
        if (p.getType() == PendingRequestType.ask_user) {
            Object value = payload == null ? null : payload.get("answer");
            if (value != null) return String.valueOf(value);
            return "[用户补料] " + JsonUtil.toJson(payload);
        }
        return "[外部事件] " + (payload == null ? "" : JsonUtil.toJson(payload));
    }

    /** 解析结果 / Resolve outcome. */
    public static final class ResolveResult {
        private final PendingRequest request;
        private final String newUserInput;
        public ResolveResult(PendingRequest r, String u) { this.request = r; this.newUserInput = u; }
        public PendingRequest getRequest() { return request; }
        public String getNewUserInput() { return newUserInput; }
    }
}
