#pragma once

// --- ImGui (needed for ImVec2, ImGui::GetTime) ---
#include "imgui.h"

// --- Editor components ---
#include "TextEditor.h"
#include "TextASTSync.h"
#include "SyntaxHighlighter.h"
#include "KeybindingManager.h"
#include "BufferManager.h"
#include "CodeEditorWidget.h"
#include "EditorMode.h"
#include "FileDialog.h"
#include "FileTree.h"
#include "WelcomeScreen.h"
#include "DragDropHandler.h"
#include "FileWatcher.h"
#include "LSPClient.h"
#include "Pipeline.h"
#include "Diagnostics.h"
#include "SettingsManager.h"
#include "LayoutManager.h"
#include "ASTMutationAPI.h"
#include "MemoryStrategyInference.h"
#include "AnnotationConflict.h"
#include "StrategyDashboard.h"
#include "TransformEngine.h"
#include "StrategyAwareOptimizer.h"
#include "CrossLanguageProjector.h"
#include "DiffUtils.h"
#include "EditorModePolicy.h"
#include "RefactorActions.h"
#include "CommandPalette.h"
#include "ContextAPI.h"
#include "BatchMutationAPI.h"
#include "Breadcrumbs.h"
#include "ProjectSearch.h"
#include "GoToLine.h"
#include "Orchestrator.h"
#include "ProjectManager.h"
#include "SessionManager.h"
#include "ZoomUtils.h"
#include "TerminalPanel.h"
#include "WebSocketServer.h"
#include "WorkflowRecorder.h"
#include "AgentRegistry.h"
#include "AgentMarketplace.h"
#include "BuildSystem.h"
#include "HelpPanel.h"
#include "Telemetry.h"
#include "UpdateChecker.h"
#include "NotificationSystem.h"
#include "UIEventBus.h"
#include "SearchUtils.h"
#include "FeatureHints.h"
#include "state/SearchState.h"
#include "state/AgentState.h"
#include "state/BuildState.h"
#include "state/LibraryState.h"
#include "state/EmacsState.h"
#include "state/UIFlags.h"
#include "state/UIAnimationState.h"
#include "DependencyPanel.h"
#include "LibraryIndexer.h"
#include "LibraryBrowserPanel.h"
#include "ImportManager.h"
#include "PrimitivesRegistry.h"
#include "AgentLibraryPolicy.h"
#include "AgentCodeGen.h"
#include "AgentAnnotationAssistant.h"
#include "AgentPermissionPolicy.h"
#include "CompositionPanel.h"
#include "IncrementalOptimizer.h"
#include "EmacsIntegration.h"
#include "EmacsPackageBrowser.h"
#include "EmacsFunctionDiscovery.h"
#include "EmacsKeybinding.h"
#include "OrgMode.h"
#include "ast/Serialization.h"
#include "ast/Generator.h"
#include "ast/Annotation.h"

// --- Standard library ---
#include <cstdio>
#include <string>
#include <vector>
#include <algorithm>
#include <fstream>
#include <sstream>
#include <cstring>
#include <cctype>
#include <climits>
#include <filesystem>
#include <map>
#include <memory>
#include <cstdlib>
#include <functional>
#include <unordered_map>
#include <nlohmann/json.hpp>
#include <sstream>

using json = nlohmann::json;

// ---------------------------------------------------------------------------
//  Forward declarations for utility functions defined in EditorUtils.h
//  (used inside EditorState inline method bodies)
// ---------------------------------------------------------------------------
static std::string generateForLanguage(const Module* ast, const std::string& language);
static std::unique_ptr<Module> cloneModule(const Module* ast);
static ASTNode* findNodeAtPosition(ASTNode* node, int line, int col);
static ASTNode* findNodeById(ASTNode* node, const std::string& id);
static int countAnnotationNodes(const ASTNode* node);

// ---------------------------------------------------------------------------
//  Simple helpers used by EditorState methods
// ---------------------------------------------------------------------------
static inline std::string bufferModeToString(BufferManager::BufferMode mode) {
    return mode == BufferManager::BufferMode::Text ? "text" : "structured";
}

static inline BufferManager::BufferMode bufferModeFromString(const std::string& mode) {
    if (mode == "text") return BufferManager::BufferMode::Text;
    if (mode == "structured") return BufferManager::BufferMode::Structured;
    return BufferManager::BufferMode::Text;
}

// ---------------------------------------------------------------------------
//  Editor application state
// ---------------------------------------------------------------------------
struct BufferState {
    TextEditor        editor;
    TextASTSync       sync;
    CodeEditorWidget  widget;
    CodeEditorWidget  generatedWidget;
    EditorMode        mode;
    EditorMode        generatedMode;
    std::string       language    = "python";
    std::string       generatedLanguage = "python";
    std::string       path        = "(untitled)";
    bool              readOnly    = false;
    BufferManager::BufferMode bufferMode = BufferManager::BufferMode::Text;
    bool              modified    = false;
    int               cursorLine  = 1;
    int               cursorCol   = 1;
    int               lspVersion  = 1;
    std::string       editBuf;
    std::string       generatedBuf;
    std::vector<HighlightSpan> highlights;
    std::vector<HighlightSpan> generatedHighlights;
    bool              highlightsDirty = true;
    bool              generatedHighlightsDirty = true;
    int               generatedHighlightLine = -1;
    float             splitScrollX = 0.0f;
    float             splitScrollY = 0.0f;
    IncrementalOptimizer incrementalOptimizer;
    Orchestrator      orchestrator;
    bool              orchestratorDirty = true;
    int               undoDepth = 0;
    size_t            fileSizeBytes = 0;
    bool              largeFileMode = false;
    bool              disableSyntaxHighlight = false;
    double            highlightRequestTime = 0.0;
    bool              pendingAstSync = false;
    double            pendingAstStart = 0.0;
};

struct EditorState {
    // Rendering context (set by main before first frame)
    ImFont*           monoFont = nullptr;
    ImFont*           uiFont = nullptr;
    float             baseFontSize = 15.0f;
    bool              fontsDirty = false;
    std::string       lastDialogPath;
    bool              exitRequested = false;

    KeybindingManager keys;
    BufferManager     buffers;
    std::map<std::string, std::unique_ptr<BufferState>> bufferStates;
    BufferState*      activeBuffer = nullptr;

    SearchState       search;
    AgentState        agent;
    BuildState        build;
    LibraryState      library;
    EmacsState        emacsState;
    UIFlags           ui;
    UIAnimationState  uiAnimations;
    bool              showLargeFilePrompt = false;
    std::string       largeFilePromptPath;
    size_t            largeFilePromptBytes = 0;

    NotificationSystem notifications;
    UIEventBus         events;
    HelpPanelState helpPanel;
    Telemetry telemetry;
    FeatureHintsState featureHints;
    char              outlineFilter[128] = {};
    WelcomeScreen     welcome;
    std::string       workspaceRoot;
    FileTree          fileTree;
    FileNode          fileTreeRoot;
    bool              fileTreeDirty = true;
    FileWatcher       watcher;
    std::shared_ptr<LSPTransport> lspTransport;
    std::shared_ptr<LSPClient> lsp;
    bool              symbolsPending = false;
    double            symbolsLastChange = 0.0;
    bool              completionPending = false;
    bool              completionVisible = false;
    int               completionSelected = 0;
    double            completionLastChange = 0.0;
    bool              lspChangePending = false;
    double            lspChangeLast = 0.0;
    std::string       lspChangePath;
    bool              hoverPending = false;
    double            hoverLastMove = 0.0;
    ImVec2            hoverLastMouse = ImVec2(-1.0f, -1.0f);
    bool              definitionPending = false;
    double            definitionLastRequest = 0.0;
    int               definitionRequestLine = 0;
    int               definitionRequestCol = 0;
    std::string       definitionRequestPath;
    double            lastFrameBudgetWarning = 0.0;
    LSPClient::DefinitionLocation definitionPreview;
    bool              definitionPreviewValid = false;
    Pipeline          pipeline;
    bool              analysisPending = false;
    double            analysisLastChange = 0.0;
    std::vector<EditorDiagnostic> whetstoneDiagnostics;
    std::vector<EditorDiagnostic> emacsDiagnostics;
    SettingsManager   settings;
    double            lastAutoSave = 0.0;
    ASTMutationAPI    mutator;
    std::vector<MemoryStrategyInference::Suggestion> suggestions;
    bool              showSuggestionPopup = false;
    MemoryStrategyInference::Suggestion suggestionPopup;
    std::string       optimizeFoldSummary;
    std::string       optimizeDeadCodeSummary;
    std::string       optimizeApplySummary;
    bool              optimizePreview = false;
    bool              showRefactorPopup = false;
    int               refactorAction = 0; // 1=rename,2=extract,3=inline
    char              refactorNameA[128] = {};
    char              refactorNameB[128] = {};
    std::string       refactorError;
    CommandPalette    commandPalette;
    std::unordered_map<std::string, std::function<void(const std::string&)>> commandHandlers;
    bool              showCommandPalette = false;
    char              commandQuery[128] = {};
    int               commandSelected = 0;
    char              commandInlineValue[64] = {};
    std::string       commandInlineCommandId;
    struct DiffState {
        bool active = false;
        bool preview = false;
        int action = 0; // 1=constant-fold, 2=dead-code-elim, 3=apply-all
        bool batch = false;
        std::string beforeText;
        std::string afterText;
        std::vector<std::string> transformIds;
        std::vector<BatchMutationAPI::Mutation> batchMutations;
        std::vector<int> beforeLines;
        std::vector<int> afterLines;
    } diff;
    CodeEditorWidget  diffLeftWidget;
    CodeEditorWidget  diffRightWidget;
    float             diffScrollX = 0.0f;
    float             diffScrollY = 0.0f;

    BufferState* active() { return activeBuffer; }
    bool isStructured() const {
        return activeBuffer && allowStructuredFeatures(activeBuffer->bufferMode);
    }
    Module* activeAST() {
        if (!activeBuffer) return nullptr;
        if (!allowStructuredFeatures(activeBuffer->bufferMode)) return nullptr;
        return activeBuffer->sync.getAST();
    }

    static std::string toFileUri(const std::string& path) {
        std::filesystem::path p(path);
        return "file:///" + p.generic_string();
    }

    static std::string fromFileUri(const std::string& uri);

    static int byteOffsetForLineCol(const std::string& text, int lineZero, int colZero);

    static int wordStartAt(const std::string& text, int pos);

    static bool isWordChar(char c) {
        return std::isalnum((unsigned char)c) || c == '_';
    }

    static std::string wordAt(const std::string& text, int pos);

    static std::string wordAtLineCol(const std::string& text, int lineZero, int colZero);

    static std::string wordPrefixAt(const std::string& text, int pos);

    void notify(NotificationLevel level, const std::string& message) {
        notifications.notify(level, message);
        events.publish(UIEventType::NotificationPosted, {}, message, ImGui::GetTime());
    }

    void notify(NotificationLevel level, const std::string& message, const NotificationTarget& target) {
        notifications.notify(level, message, target);
        events.publish(UIEventType::NotificationPosted, target.path, message, ImGui::GetTime());
    }

    void navigateToTarget(const NotificationTarget& target);

    void jumpTo(BufferState* buf, int lineZero, int colZero) {
        if (!buf) return;
        int pos = byteOffsetForLineCol(buf->editBuf, lineZero, colZero);
        buf->widget.setCursor(pos);
        buf->cursorLine = lineZero + 1;
        buf->cursorCol = colZero + 1;
    }

    void applyCompletion(BufferState* buf, const std::string& insertText, int replaceStart, int replaceEnd);

    void insertTextAtCursor(const std::string& text);

    void ensureImportForSymbol(const std::string& library, const std::string& symbol);

    void appendUnusedImportDiagnostics(std::vector<EditorDiagnostic>& diags);

    struct ImportLocation {
        int line = 0;
        std::string library;
    };

    static PackageEcosystem ecosystemForLanguage(const std::string& language);

    static std::vector<ImportLocation> collectImportLocations(const std::string& text,
                                                              const std::string& language);

    std::string dependencyVersionFor(const std::string& osvEco,
                                     const std::string& package) const;

    void appendVulnerabilityDiagnostics(std::vector<EditorDiagnostic>& diags);

    std::string makeUntitledName() const;

    void createBuffer(const std::string& path, const std::string& content,
                      const std::string& language,
                      BufferManager::BufferMode mode = BufferManager::BufferMode::Text,
                      size_t fileSizeBytes = 0,
                      bool largeFileMode = false,
                      bool disableSyntaxHighlight = false,
                      bool deferAstSync = false);
    BufferManager::BufferMode defaultBufferMode() const;

    bool jumpToDefinitionLocation(const LSPClient::DefinitionLocation& loc);

    bool goToDefinitionFallback(int lineZero, int colZero);

    bool goToDefinitionAt(int lineZero, int colZero);

    bool previewDefinitionAt(int lineZero, int colZero, std::string& outLabel);

    void switchToBuffer(const std::string& path);

    static std::string osLabel();

    std::string detectProjectType() const;

    json buildSessionMetadata(bool autoRecording) const;

    void startSessionRecording(const std::string& name, bool autoRecording);

    void stopSessionRecording();

    void toggleSessionRecording();

    static std::string uiEventLabel(UIEventType type);

    void recordUIEvent(const UIEvent& ev);

    void recordEditorEvent(const std::string& type, const json& payload);

    std::filesystem::path configDir() const;

    std::filesystem::path recentFilePath() const;

    void loadRecentFiles();

    void saveRecentFiles();

    void closeAllBuffers();

    bool renameBufferPath(const std::string& oldPath, const std::string& newPath);

    bool saveProject(const std::string& path);

    bool loadProject(const std::string& path);

    std::filesystem::path sessionFilePath() const;

    std::filesystem::path settingsFilePath() const;

    bool saveSession(const std::string& imguiIni);

    bool loadSession(SessionData& out);

    void applySession(const SessionData& session);

    void applyTabSizeToBuffers(int size);

    void setActiveBufferMode(BufferManager::BufferMode mode);

    void queueLspDidChange();

    void flushLspDidChange(double nowSeconds);

    void flushDeferredAstSync(double nowSeconds);

    bool hasSidePanels() const;

    void cyclePanelFocus();

    void applySettingsToState();

    void loadSettingsFromDisk();

    void saveSettingsToDisk();

    void init();

    std::string orgTempExtension(const std::string& language) const;

    std::string writeOrgTempFile(const std::string& language, const std::string& code);

    std::string runOrgBlock(const std::string& language, const std::string& code);

    void startEmacsDaemonFromSettings();

    std::string resolveEmacsInitPath() const;

    void addEmacsLogDiagnostics(const std::string& log);

    void initAgentServer();

    void shutdownAgentServer();

    void logAgentEvent(const std::string& msg);

    AgentRole getAgentRole(const std::string& sessionId) const;

    void setAgentRole(const std::string& sessionId, AgentRole role);

    std::string agentActorLabel(const std::string& sessionId) const;

    void refreshBuildSystem();

    int runBuildCommand(const BuildCommand& cmd);

    void pollLspMessages();

    void requestLibraryIndex();

    void processLibraryIndexResponses();

    void updateEmacsFunctionIndex();

    void rebuildExternalModulesFromIndex();

    json buildDiagnosticsJson() const;

    json processAgentRequest(const json& request, const std::string& sessionId);


    void syncOrchestratorFromActive();

    void applyOrchestratorToActive();

    Module* mutationAST();

    void recordUndoSnapshot();

    void applySnapshotToActive(const std::string& text, std::unique_ptr<Module> ast);

    void registerCommand(const std::string& id,
                         const std::string& label,
                         const std::string& shortcut,
                         const std::string& category,
                         int contextMask,
                         std::function<void()> fn);

    void registerCommand(const std::string& id,
                         const std::string& label,
                         const std::string& shortcut,
                         const std::string& category,
                         int contextMask,
                         const std::string& inputHint,
                         std::function<void(const std::string&)> fn);

    void registerCommands();

    void executeCommand(const std::string& id, const std::string& arg = {});

    void setLanguage(const std::string& lang);

    bool handleEmacsKeyChord(const std::string& chord);

    void refreshEmacsModeLine(double nowSeconds);

    void pullFromEmacs();

    void pushToEmacs();

    void openInEmacsFrame();

    std::string buildRunCommand(const std::string& path,
                                const std::string& language,
                                bool buildOnly) const;

    bool runActiveFile(bool buildOnly);

    // Called after editBuf changes (from ImGui input)
    void onTextChanged();

    void doUndo();

    void doRedo();

    SearchOptions currentSearchOptions() const;

    void addSearchHistory(const std::string& query);

    bool selectionRange(int& outStart, int& outEnd);

    void refreshSearchMatches();

    bool moveToMatchIndex(int index);

    void doFindNext(bool backwards = false);

    void doReplaceCurrent();

    void doFind();

    void doReplaceAll();

    void doSave();

    void doOpen(const std::string& path,
                BufferManager::BufferMode modeOverride = BufferManager::BufferMode::Text,
                bool deferAstSync = false);

    void reloadBuffer(const std::string& path);

    void handleFileChanges();

    void updateHighlights();

    void updateGenerated();

    void projectToLanguage(const std::string& targetLanguage);

    void refreshActiveTextFromAST();

    void openDiff(const std::string& beforeText,
                  const std::string& afterText,
                  bool preview,
                  int action,
                  const std::vector<std::string>& transformIds,
                  const std::vector<BatchMutationAPI::Mutation>& batchMutations = {});

    void updateCursorPos(int bytePos);

    void refreshFileTree();
};

// --- LspOps (extracted Step 238) ---
#include "LspOps.h"

// --- EmacsOps (extracted Step 239) ---
#include "EmacsOps.h"

// --- EditOps (extracted Step 237) ---
#include "EditOps.h"

// --- BufferOps (extracted Step 236) ---
#include "BufferOps.h"

// --- AgentRPCHandler (extracted Step 235) ---
#include "AgentRPCHandler.h"

inline json EditorState::processAgentRequest(const json& request,
                                             const std::string& sessionId) {
    return handleAgentRequest(*this, request, sessionId);
}


struct NullLSPTransport : public LSPTransport {
    void send(const std::string& msg) override { (void)msg; }
    bool receive(std::string& out) override { (void)out; return false; }
    bool isOpen() const override { return false; }
    void close() override {}
};

