#pragma once
// Step 382: Orchestrator RPC surface for headless agent requests.

#include "HeadlessEditorState.h"
#include "ResultAcceptance.h"
#include <optional>

static inline json orchestratorRpcError(const json& id, int code,
                                        const std::string& msg) {
    return {{"jsonrpc", "2.0"}, {"id", id},
            {"error", {{"code", code}, {"message", msg}}}};
}

static inline json orchestratorRpcResult(const json& id, const json& result) {
    return {{"jsonrpc", "2.0"}, {"id", id}, {"result", result}};
}

static inline json orchestratorEventToJson(const OrchestratorEvent& event) {
    return {
        {"type", event.type},
        {"itemId", event.itemId},
        {"detail", event.detail},
        {"timestamp", event.timestamp}
    };
}

static inline json orchestratorEventsToJson(
    const std::vector<OrchestratorEvent>& events) {
    json arr = json::array();
    for (const auto& event : events) arr.push_back(orchestratorEventToJson(event));
    return arr;
}

static inline json orchestratorBlockersToJson(const std::vector<BlockerInfo>& blockers) {
    json arr = json::array();
    for (const auto& b : blockers) {
        arr.push_back({
            {"type", b.type},
            {"itemIds", b.itemIds},
            {"description", b.description}
        });
    }
    return arr;
}

static inline std::string streamTypeForOrchestratorType(const std::string& type) {
    if (type == "routed") return "task.routed";
    if (type == "context-assembled") return "task.context-assembled";
    if (type == "executed") return "task.executed";
    if (type == "auto-approved") return "task.auto-approved";
    if (type == "sent-to-review") return "task.sent-to-review";
    if (type == "completed") return "task.completed";
    if (type == "rejected") return "task.rejected";
    if (type == "escalated") return "task.escalated";
    if (type == "blocked") return "workflow.blocked";
    return type;
}

static inline void emitWorkflowEvents(HeadlessEditorState& state,
                                      const std::vector<OrchestratorEvent>& events) {
    for (const auto& event : events) {
        OrchestratorEvent mapped = event;
        mapped.type = streamTypeForOrchestratorType(event.type);
        state.eventStream.emit(mapped);
    }
}

static inline std::map<std::string, BufferInfo> collectOrchestratorBufferInfos(
    HeadlessEditorState& state) {
    std::map<std::string, BufferInfo> infos;
    for (const auto& [path, buf] : state.bufferStates) {
        BufferInfo bi;
        bi.path = path;
        bi.content = buf->editBuf;
        Module* ast = buf->sync.getAST();
        bi.compactAst = ast ? toJsonCompactSummary(ast) : json::array();
        bi.importGraph = json::object();
        infos[path] = std::move(bi);
    }
    return infos;
}

inline std::optional<json> tryHandleHeadlessOrchestratorRPC(
    HeadlessEditorState& state,
    const json& request,
    const json& id,
    const std::string& method,
    AgentRole role) {
    if (method == "orchestrateStep") {
        if (!AgentPermissionPolicy::canInvoke(role, method))
            return orchestratorRpcError(id, -32031, "Role not permitted");
        if (!state.workflow)
            return orchestratorRpcError(id, -32000, "No active workflow");

        WorkflowOrchestrator orchestrator(*state.workflow, state.routingEngine,
                                          state.workerRegistry, state.contextAssembler,
                                          state.reviewGate);
        orchestrator.setReviewPolicy(state.reviewPolicy);
        orchestrator.setBuffers(collectOrchestratorBufferInfos(state));

        OrchestratorEvent event = orchestrator.step();
        if (!state.workflowProgress) {
            state.workflowProgress = WorkflowProgress(state.workflow->getStats().total);
        }
        state.workflowProgress->recordEvent(event);
        emitWorkflowEvents(state, {event});
        return orchestratorRpcResult(id, {{"event", orchestratorEventToJson(event)}});
    }

    if (method == "orchestrateAdvance") {
        if (!AgentPermissionPolicy::canInvoke(role, method))
            return orchestratorRpcError(id, -32031, "Role not permitted");
        if (!state.workflow)
            return orchestratorRpcError(id, -32000, "No active workflow");

        WorkflowOrchestrator orchestrator(*state.workflow, state.routingEngine,
                                          state.workerRegistry, state.contextAssembler,
                                          state.reviewGate);
        orchestrator.setReviewPolicy(state.reviewPolicy);
        orchestrator.setBuffers(collectOrchestratorBufferInfos(state));

        BatchResult batch = orchestrator.advanceBatch();
        if (!state.workflowProgress) {
            state.workflowProgress = WorkflowProgress(state.workflow->getStats().total);
        }
        for (const auto& event : batch.events) {
            state.workflowProgress->recordEvent(event);
        }
        emitWorkflowEvents(state, batch.events);

        return orchestratorRpcResult(id, {
            {"events", orchestratorEventsToJson(batch.events)},
            {"itemsAdvanced", batch.itemsAdvanced},
            {"itemsBlocked", batch.itemsBlocked},
            {"contextTokensSaved", batch.contextTokensSaved}
        });
    }

    if (method == "orchestrateRunDeterministic") {
        if (!AgentPermissionPolicy::canInvoke(role, method))
            return orchestratorRpcError(id, -32031, "Role not permitted");
        if (!state.workflow)
            return orchestratorRpcError(id, -32000, "No active workflow");

        WorkflowOrchestrator orchestrator(*state.workflow, state.routingEngine,
                                          state.workerRegistry, state.contextAssembler,
                                          state.reviewGate);
        orchestrator.setReviewPolicy(state.reviewPolicy);
        orchestrator.setBuffers(collectOrchestratorBufferInfos(state));

        std::vector<OrchestratorEvent> allEvents;
        int totalAdvanced = 0;
        int totalBlocked = 0;
        int totalSaved = 0;
        while (true) {
            BatchResult batch = orchestrator.advanceBatch();
            if (batch.events.empty()) break;
            bool anyProgress = false;
            for (const auto& event : batch.events) {
                if (event.type != "blocked") anyProgress = true;
                allEvents.push_back(event);
            }
            totalAdvanced += batch.itemsAdvanced;
            totalBlocked += batch.itemsBlocked;
            totalSaved += batch.contextTokensSaved;
            if (!anyProgress) break;
        }

        if (!state.workflowProgress) {
            state.workflowProgress = WorkflowProgress(state.workflow->getStats().total);
        }
        for (const auto& event : allEvents) {
            state.workflowProgress->recordEvent(event);
        }
        emitWorkflowEvents(state, allEvents);

        return orchestratorRpcResult(id, {
            {"events", orchestratorEventsToJson(allEvents)},
            {"itemsAdvanced", totalAdvanced},
            {"itemsBlocked", totalBlocked},
            {"contextTokensSaved", totalSaved},
            {"stats", state.workflow->getStats().toJson()},
            {"blockers", orchestratorBlockersToJson(orchestrator.getBlockers())}
        });
    }

    if (method == "getBlockers") {
        if (!AgentPermissionPolicy::canInvoke(role, method))
            return orchestratorRpcError(id, -32031, "Role not permitted");
        if (!state.workflow)
            return orchestratorRpcError(id, -32000, "No active workflow");

        WorkflowOrchestrator orchestrator(*state.workflow, state.routingEngine,
                                          state.workerRegistry, state.contextAssembler,
                                          state.reviewGate);
        orchestrator.setReviewPolicy(state.reviewPolicy);
        orchestrator.setBuffers(collectOrchestratorBufferInfos(state));
        auto blockers = orchestrator.getBlockers();
        return orchestratorRpcResult(id, {
            {"blockers", orchestratorBlockersToJson(blockers)},
            {"count", (int)blockers.size()}
        });
    }

    if (method == "getProgress") {
        if (!AgentPermissionPolicy::canInvoke(role, method))
            return orchestratorRpcError(id, -32031, "Role not permitted");
        if (!state.workflow)
            return orchestratorRpcError(id, -32000, "No active workflow");

        if (!state.workflowProgress) {
            state.workflowProgress = WorkflowProgress(state.workflow->getStats().total);
        } else {
            state.workflowProgress->setTotalItems(state.workflow->getStats().total);
        }
        ProgressSnapshot snapshot = state.workflowProgress->getSnapshot();
        if (snapshot.blockers.empty()) {
            WorkflowOrchestrator orchestrator(*state.workflow, state.routingEngine,
                                              state.workerRegistry, state.contextAssembler,
                                              state.reviewGate);
            orchestrator.setReviewPolicy(state.reviewPolicy);
            orchestrator.setBuffers(collectOrchestratorBufferInfos(state));
            snapshot.blockers = orchestrator.getBlockers();
        }
        state.eventStream.emit({
            "workflow.progress",
            "",
            snapshot.toJson(),
            workItemTimestamp()
        });
        return orchestratorRpcResult(id, snapshot.toJson());
    }

    if (method == "submitExternalResult") {
        if (!AgentPermissionPolicy::canInvoke(role, method))
            return orchestratorRpcError(id, -32031, "Role not permitted");
        if (!state.workflow)
            return orchestratorRpcError(id, -32000, "No active workflow");
        auto params = request.contains("params") ? request["params"] : json::object();
        std::string itemId = params.value("itemId", "");
        if (itemId.empty())
            return orchestratorRpcError(id, -32602, "Missing itemId");
        if (!params.contains("result") || !params["result"].is_object())
            return orchestratorRpcError(id, -32602, "Missing result payload");

        auto item = state.workflow->queue.getItem(itemId);
        if (!item)
            return orchestratorRpcError(id, -32602, "Work item not found");
        if (!(item->workerType == "slm" || item->workerType == "llm"))
            return orchestratorRpcError(id, -32000, "submitExternalResult only supports slm/llm items");
        if (item->status != WI_IN_PROGRESS &&
            item->status != WI_ASSIGNED &&
            item->status != WI_REVIEW)
            return orchestratorRpcError(id, -32000,
                "Item must be assigned, in-progress, or review for external submission");

        WorkItem updated = *item;
        if (updated.status == WI_ASSIGNED) transitionWorkItem(updated, WI_IN_PROGRESS);
        if (updated.status == WI_REVIEW) {
            updated.status = WI_IN_PROGRESS;
        }

        ResultSubmission submission;
        submission.itemId = itemId;
        submission.generatedCode = params["result"].value("generatedCode", "");
        submission.confidence = params["result"].value("confidence", 0.0f);
        submission.reasoning = params["result"].value("reasoning", "");
        if (params["result"].contains("suggestedAnnotations")) {
            submission.suggestedAnnotations =
                params["result"]["suggestedAnnotations"].get<std::vector<json>>();
        }

        std::string language = state.activeBuffer ? state.activeBuffer->language
                                                  : state.defaultLanguage;
        WorkItemResult evaluatedResult;
        ResultAcceptance acceptance = evaluateResultSubmission(
            submission, updated, language, state.reviewGate, state.reviewPolicy, evaluatedResult);
        updated.result = evaluatedResult;
        state.workflow->queue.updateItem(itemId, updated);

        std::vector<OrchestratorEvent> events;
        events.push_back({
            "executed",
            itemId,
            {{"workerType", updated.workerType},
             {"confidence", updated.result.confidence},
             {"tokensGenerated", updated.result.tokensGenerated}},
            workItemTimestamp()
        });

        if (!acceptance.validationPassed) {
            WorkflowOrchestrator orchestrator(*state.workflow, state.routingEngine,
                                              state.workerRegistry, state.contextAssembler,
                                              state.reviewGate);
            orchestrator.setReviewPolicy(state.reviewPolicy);
            orchestrator.setBuffers(collectOrchestratorBufferInfos(state));
            std::string feedback = acceptance.validationErrors.empty()
                ? "validation failed"
                : acceptance.validationErrors.front();
            orchestrator.rejectAndRequeue(itemId, feedback, "validator");
            events.push_back({"rejected", itemId,
                              {{"reason", feedback}},
                              workItemTimestamp()});
            events.push_back({"escalated", itemId,
                              {{"reason", "validation-failed-requeue"}},
                              workItemTimestamp()});
        } else if (acceptance.autoApproved) {
            state.workflow->queue.complete(itemId);
            events.push_back({"auto-approved", itemId,
                              {{"rule", "acceptance-auto-approve"}},
                              workItemTimestamp()});
            events.push_back({"completed", itemId, json::object(), workItemTimestamp()});
        } else {
            auto maybeUpdated = state.workflow->queue.getItem(itemId);
            if (!maybeUpdated) {
                return orchestratorRpcError(id, -32000, "Failed to reload item");
            }
            WorkItem reviewItem = *maybeUpdated;
            transitionWorkItem(reviewItem, WI_REVIEW);
            state.workflow->queue.updateItem(itemId, reviewItem);
            events.push_back({"sent-to-review", itemId,
                              {{"reason", "validation passed, awaiting review"}},
                              workItemTimestamp()});
        }

        if (!state.workflowProgress) {
            state.workflowProgress = WorkflowProgress(state.workflow->getStats().total);
        }
        for (const auto& event : events) {
            state.workflowProgress->recordEvent(event);
        }
        emitWorkflowEvents(state, events);

        if (state.workflow->getStats().total > 0 &&
            state.workflow->getStats().complete == state.workflow->getStats().total) {
            state.eventStream.emit({"workflow.complete", "", json::object(),
                                    workItemTimestamp()});
        }

        auto latest = state.workflow->queue.getItem(itemId);
        return orchestratorRpcResult(id, {
            {"success", true},
            {"acceptance", acceptance.toJson()},
            {"events", orchestratorEventsToJson(events)},
            {"item", latest ? workItemToJson(*latest) : json::object()}
        });
    }

    if (method == "getEventStream") {
        if (!AgentPermissionPolicy::canInvoke(role, method))
            return orchestratorRpcError(id, -32031, "Role not permitted");
        auto params = request.contains("params") ? request["params"] : json::object();
        int sinceVersion = params.value("sinceVersion", 0);
        auto events = state.eventStream.poll(sinceVersion);
        return orchestratorRpcResult(id, {
            {"version", state.eventStream.getVersion()},
            {"events", EventStream::toJson(events)}
        });
    }

    if (method == "getRecentEvents") {
        if (!AgentPermissionPolicy::canInvoke(role, method))
            return orchestratorRpcError(id, -32031, "Role not permitted");
        auto params = request.contains("params") ? request["params"] : json::object();
        int count = params.value("count", 20);
        auto events = state.eventStream.getRecent(count);
        return orchestratorRpcResult(id, {
            {"version", state.eventStream.getVersion()},
            {"events", EventStream::toJson(events)}
        });
    }

    return std::nullopt;
}
