@page "/editor"
@page "/editor/{action}"
@page "/editor/{action}/{projectId:int}"
@using System.Threading
@inject IOllamaService OllamaService
@inject IProjectService ProjectService
@inject IRagService RagService
@inject ISettingsService SettingsService
@inject IWikipediaService WikipediaService
@inject NavigationManager Navigation
@inject ILocalizationService Loc
@inject IAuthService AuthService
@inject OllamaSettings OllamaSettings
@inject OperationState OpState
@inject IJSRuntime JS

@implements IDisposable
@rendermode InteractiveServer
@attribute [Authorize]

<PageTitle>@Loc["page.title.editor"]</PageTitle>

<div class="editor-page">
    <textarea class="editor-area"
              @oninput="OnContentChanged"
              value="@editorContent"
              placeholder="@Loc["editor.placeholder.content"]"></textarea>
    <div class="editor-format-toolbar">
        <button class="fmt-btn" @onclick="() => InsertHeading(1)" title="Überschrift 1">H1</button>
        <button class="fmt-btn" @onclick="() => InsertHeading(2)" title="Überschrift 2">H2</button>
        <button class="fmt-btn" @onclick="() => InsertHeading(3)" title="Überschrift 3">H3</button>
        <button class="fmt-btn" @onclick="() => InsertHeading(4)" title="Überschrift 4">H4</button>
        <button class="fmt-btn" @onclick="() => InsertHeading(5)" title="Überschrift 5">H5</button>
        <span class="fmt-sep"></span>
        <button class="fmt-btn" @onclick='() => WrapSelection("**", "**")' title="Fett"><b>B</b></button>
        <button class="fmt-btn" @onclick='() => WrapSelection("*", "*")' title="Kursiv"><i>I</i></button>
        <button class="fmt-btn" @onclick='() => WrapSelection("<u>", "</u>")' title="Unterstrichen"><u>U</u></button>
        <span class="fmt-sep"></span>
        <button class="fmt-btn" @onclick="() => InsertList(false)" title="Aufzählung">UL</button>
        <button class="fmt-btn" @onclick="() => InsertList(true)" title="Nummerierte Liste">OL</button>
        <span class="fmt-sep"></span>
        <button class="fmt-btn" @onclick="() => IndentLines()" title="Einrücken">→</button>
        <button class="fmt-btn" @onclick="() => OutdentLines()" title="Ausrücken">←</button>
    </div>
    <CopyCreateToolbar Text="@editorContent" DefaultFileName="editor_text" />

    <div class="editor-toolbar">
        <span style="flex: 1; font-size: 12px; color: var(--border-color); display:flex;align-items:center;gap:8px;">
            @if (_contentModified)
            {
                <span class="editor-modified-badge">●</span>
            }
            @statusText
        </span>
        @if (isChecking)
        {
            <progress value="@checkProgress" max="100"></progress>
        }
        <button @onclick="CheckStyle" disabled="@isChecking">@Loc["editor.button.style_check"]</button>
        <button @onclick="SpellCheck" disabled="@isChecking">@Loc["editor.button.spell_check"]</button>
        <button @onclick="SaveCurrentFile" disabled="@(string.IsNullOrEmpty(currentFile))">@Loc["editor.button.save"]</button>
        <button @onclick="ShowVersionDialog" disabled="@(string.IsNullOrEmpty(currentFile))">@Loc["editor.version.button.save"]</button>
        <button @onclick="ShowVersionBrowser" disabled="@(string.IsNullOrEmpty(currentFile))">@Loc["editor.version.button.browse"]</button>
        <button @onclick="OpenAssistant">@Loc["editor.button.assistant"]</button>
        <button @onclick="IndexProject" disabled="@isChecking">@Loc["editor.button.index_project"]</button>
    </div>

    @if (showVersionDialog)
    {
        <div class="modal-overlay">
            <div class="modal-dialog">
                <div class="modal-title">@Loc["editor.version.title"]</div>
                <div class="modal-body">
                    <div class="form-group">
                        <label>@Loc["editor.version.label.comment"]</label>
                        <input @bind="versionComment" placeholder="@Loc["editor.version.placeholder.comment"]" />
                    </div>
                </div>
                <div class="modal-actions">
                    <button @onclick="() => showVersionDialog = false">@Loc["common.cancel"]</button>
                    <button @onclick="SaveVersioned" class="primary">@Loc["editor.version.button.save"]</button>
                </div>
            </div>
        </div>
    }

    <div class="search-section">
        <input value="@searchQuery" @oninput="HandleSearchInput" placeholder="@Loc["editor.search.placeholder"]"
               disabled="@isChecking" />
        <button @onclick="RagSearch" disabled=@buttonDisabled>@Loc["editor.button.search_rag"]</button>
        <button @onclick="SearchWikipedia" disabled=@buttonDisabled>@Loc["editor.button.search_wikipedia"]</button>
    </div>

    @if (showSpellDiff && !string.IsNullOrEmpty(correctedText))
    {
        <div class="results-area">
            <div class="diff-panel">
                <h4 style="display:inline">@Loc["editor.spellcheck.heading"]</h4>
                <CopyCreateToolbar Text="@correctedText" DefaultFileName="korrigierter_text" />
                <DiffView Original="@originalText" Corrected="@correctedText" />
                <div class="diff-actions">
                    <button class="btn btn-primary" @onclick="ApplySpellCorrections">@Loc["editor.spellcheck.button.apply"]</button>
                </div>
            </div>
        </div>
    }
    else if (showVersionDiff && _selectedVersion != null)
    {
        <div class="results-area">
            <div class="diff-panel">
                <h4>@Loc["editor.version.diff_title"] @_selectedVersion.Timestamp:dd.MM.yyyy HH:mm</h4>
                @if (!string.IsNullOrEmpty(_selectedVersion.Comment))
                {
                    <p style="font-size:11px;color:var(--border-color);margin:0 0 8px 0;">@Loc["editor.version.label.comment"]: @_selectedVersion.Comment</p>
                }
                <DiffView Original="@editorContent" Corrected="@_versionDiffContent" />
                <div class="diff-actions">
                    <button class="btn btn-primary" @onclick="() => RestoreVersion(_selectedVersion)">@Loc["editor.version.button.restore"]</button>
                    <button class="btn btn-secondary" @onclick="CloseVersionBrowser">@Loc["common.cancel"]</button>
                </div>
            </div>
        </div>
    }
    else if (showVersionBrowser)
    {
        <div class="results-area">
            <h4>@Loc["editor.version.list_title"]</h4>
            @if (_versionList == null || _versionList.Count == 0)
            {
                <p>@Loc["editor.version.empty"]</p>
            }
            else
            {
                <div class="version-list">
                    @foreach (var v in _versionList)
                    {
                        <div class="version-item @(v == _selectedVersion ? "active" : "")">
                            <div class="version-item-info">
                                <span class="version-date">@v.Timestamp.ToString("dd.MM.yyyy HH:mm")</span>
                                @if (!string.IsNullOrEmpty(v.Comment))
                                {
                                    <span class="version-comment">@v.Comment</span>
                                }
                            </div>
                            <div class="version-item-actions">
                                <button class="btn btn-sm" @onclick="() => ShowVersionDiff(v)">@Loc["editor.version.button.diff"]</button>
                                <button class="btn btn-sm btn-primary" @onclick="() => RestoreVersion(v)">@Loc["editor.version.button.restore"]</button>
                            </div>
                        </div>
                    }
                </div>
            }
            <button class="btn btn-secondary" @onclick="CloseVersionBrowser">@Loc["common.cancel"]</button>
        </div>
    }
    else if (ragResults != null && ragResults.Count > 0)
    {
        <div class="results-area">
            <h4>@Loc["editor.rag.heading"]</h4>
            @foreach (var r in ragResults)
            {
                <div class="fact-card">
                    <div class="fact-similarity">@Loc["editor.rag.label.similarity"] @r.Similarity.ToString("P1")</div>
                    <div class="fact-path">@r.FilePath</div>
                    <div class="fact-text">@r.Text</div>
                </div>
            }
        </div>
    }
    else if (wikipediaResults != null && wikipediaResults.Count > 0)
    {
        <div class="results-area">
            <h4>@Loc["editor.wikipedia.heading"]</h4>
            @foreach (var r in wikipediaResults)
            {
                <div class="fact-card">
                    <div class="fact-similarity">@(r.Language == "de" ? "🇩🇪" : "🇬🇧") @r.Language.ToUpper()</div>
                    <div class="fact-path">
                        <a href="@r.Url" target="_blank">@r.Title</a>
                    </div>
                    <div class="fact-text">@r.Snippet</div>
                    @if (!string.IsNullOrEmpty(r.Extract))
                    {
                        <div class="fact-extract">@r.Extract</div>
                    }
                    <div class="fact-actions">
                        <button @onclick="() => LoadFullArticle(r)" disabled="@(loadedArticles.ContainsKey(r.Url))">
                            @(loadedArticles.ContainsKey(r.Url)
                                ? Loc["editor.wikipedia.status.loaded"]
                                : Loc["editor.wikipedia.button.load"])
                        </button>
                        <button @onclick="() => SaveAsFact(r)" disabled="@(string.IsNullOrWhiteSpace(r.Extract) || currentProjectId == 0)">
                            @Loc["editor.wikipedia.button.save_as_fact"]
                        </button>
                    </div>
                </div>
            }
        </div>
    }
    else if (!string.IsNullOrEmpty(analysisResult) && !showVersionBrowser)
    {
        <div class="results-area">
            <h4 style="display:inline">@Loc["editor.result.heading"]</h4>
            <CopyCreateToolbar Text="@analysisResult" />
            <div class="result-text">@analysisResult</div>
        </div>
    }
</div>

@if (showCreateDialog)
{
    <div class="modal-overlay">
        <div class="modal-dialog">
            <div class="modal-title">@Loc["editor.create_project.title"]</div>
            <div class="modal-body">
                <div class="form-group">
                    <label>@Loc["editor.create_project.label.name"]</label>
                    <input @bind="newProjectName" placeholder="@Loc["editor.create_project.placeholder.name"]" />
                </div>
                <div class="form-group">
                    <label>@Loc["editor.create_project.label.path"]</label>
                    <input @bind="newProjectPath" placeholder="@Loc["editor.create_project.placeholder.path"]" />
                    <small class="form-hint">Erlaubter Pfad: @ProjectPathHelper.AllowedBasePath</small>
                </div>
                <div class="form-group">
                    <label>Vorlage</label>
                    <div class="template-radio-group">
                        <label class="template-radio">
                            <input type="radio" name="template" value="@ProjectTemplate.Novel" checked="@(selectedTemplate == ProjectTemplate.Novel)" @onchange="() => selectedTemplate = ProjectTemplate.Novel" />
                            <span>📖 Roman</span>
                        </label>
                        <label class="template-radio">
                            <input type="radio" name="template" value="@ProjectTemplate.Technical" checked="@(selectedTemplate == ProjectTemplate.Technical)" @onchange="() => selectedTemplate = ProjectTemplate.Technical" />
                            <span>⚙️ Technische Vorlage (Requirements)</span>
                        </label>
                    </div>
                </div>
            </div>
            <div class="modal-actions">
                <button @onclick="CancelCreateProject">@Loc["common.cancel"]</button>
                <button @onclick="CreateProject" disabled="@(() => string.IsNullOrWhiteSpace(newProjectName) || string.IsNullOrWhiteSpace(newProjectPath))" class="primary">@Loc["editor.button.create"]</button>
            </div>
        </div>
    </div>
}

@code {
    [Parameter]
    public string? Action { get; set; }

    [Parameter]
    public int? ProjectId { get; set; }

    private string editorContent = string.Empty;
    private string searchQuery = string.Empty;
    private string statusText = string.Empty;
    private bool isChecking;
    private bool buttonDisabled;
    private double checkProgress;
    private string analysisResult = string.Empty;
    private string originalText = string.Empty;
    private string correctedText = string.Empty;
    private bool showSpellDiff;
    private List<RagSearchResult>? ragResults;
    private List<WikipediaResult>? wikipediaResults;

    private int currentProjectId;
    private string? currentFile;

    private bool showCreateDialog;
    private string newProjectName = string.Empty;
    private string newProjectPath = string.Empty;
    private ProjectTemplate selectedTemplate = ProjectTemplate.Novel;
    private Dictionary<string, bool> loadedArticles = new();
    private bool showVersionDialog;
    private string versionComment = string.Empty;
    private VersionEntry? latestVersion;

    private Timer? _autosaveTimer;
    private string _lastSavedContent = string.Empty;
    private bool _contentModified;
    private bool showVersionBrowser;
    private bool showVersionDiff;
    private List<VersionEntry> _versionList = new();
    private VersionEntry? _selectedVersion;
    private string _versionDiffContent = string.Empty;

    private record VersionEntry(string FilePath, DateTime Timestamp, string? Comment);

    protected override async Task OnInitializedAsync()
    {
        statusText = Loc["common.status.ready"];

        Navigation.LocationChanged += OnLocationChanged;

        await LoadProjectContext();

        var pendingOutput = SettingsService.PendingAssistantOutput;
        if (!string.IsNullOrEmpty(pendingOutput))
        {
            SettingsService.PendingAssistantOutput = null;
            editorContent = pendingOutput;
            _lastSavedContent = editorContent;
            statusText = Loc["editor.status.imported_from_assistant"];
        }

        if (string.Equals(Action, "newproject", StringComparison.OrdinalIgnoreCase))
        {
            newProjectName = string.Empty;
            newProjectPath = ProjectPathHelper.SuggestPath("Projekt");
            showCreateDialog = true;
        }
    }

    private async void OnLocationChanged(object? sender, LocationChangedEventArgs e)
    {
        await LoadProjectContext();
        StateHasChanged();
    }

    public void Dispose()
    {
        Navigation.LocationChanged -= OnLocationChanged;
        _autosaveTimer?.Dispose();
    }

    private void OnContentChanged(ChangeEventArgs e)
    {
        editorContent = e.Value?.ToString() ?? string.Empty;
        _contentModified = editorContent != _lastSavedContent;
        ResetAutosaveTimer();
        StateHasChanged();
    }

    private void ResetAutosaveTimer()
    {
        _autosaveTimer?.Dispose();
        _autosaveTimer = new Timer(AutosaveCallback, null, TimeSpan.FromSeconds(2), Timeout.InfiniteTimeSpan);
    }

    private async void AutosaveCallback(object? state)
    {
        if (string.IsNullOrEmpty(currentFile) || !_contentModified || isChecking) return;

        try
        {
            await InvokeAsync(async () =>
            {
                await File.WriteAllTextAsync(currentFile, editorContent);
                _lastSavedContent = editorContent;
                _contentModified = false;
                statusText = Loc["editor.status.autosaved"];
                StateHasChanged();
            });
        }
        catch
        {
        }
    }

    private void OpenAssistant()
    {
        SettingsService.PendingAssistantInput = editorContent;
        Navigation.NavigateTo("/assistant");
    }

    private void HandleSearchInput(ChangeEventArgs e)
    {
        searchQuery = e.Value?.ToString() ?? string.Empty;
        buttonDisabled = isChecking || string.IsNullOrWhiteSpace(searchQuery);
        StateHasChanged();
    }

    private async Task LoadProjectContext()
    {
        if (ProjectId.HasValue)
        {
            currentProjectId = ProjectId.Value;
            SettingsService.CurrentProjectId = currentProjectId;
        }
        else if (SettingsService.CurrentProjectId.HasValue)
        {
            currentProjectId = SettingsService.CurrentProjectId.Value;
        }

        if (currentProjectId > 0)
            await SettingsService.LoadProjectSettingsAsync();

        var filePath = SettingsService.CurrentFilePath;
        if (!string.IsNullOrEmpty(filePath) && File.Exists(filePath))
        {
            currentFile = filePath;
            editorContent = await File.ReadAllTextAsync(filePath);
            _lastSavedContent = editorContent;
            _contentModified = false;
            statusText = $"{Loc["editor.status.loaded"]} {Path.GetFileName(filePath)}";
            RefreshLatestVersion();

            var styleModel = await SettingsService.GetStyleModelAsync();
            if (!string.IsNullOrEmpty(styleModel))
            {
                var ctx = await OllamaService.GetModelContextWindowAsync(styleModel);
                if (ctx.Success && editorContent.Length > ctx.ContextWindow * 0.8)
                {
                    statusText = $"⚠ {Loc["editor.warning.context"]} ({editorContent.Length} Tokens ≈ {ctx.ContextWindow} Kontext)";
                }
            }
        }
    }

    private void ClearResults()
    {
        showSpellDiff = false;
        showVersionBrowser = false;
        showVersionDiff = false;
        ragResults = null;
        wikipediaResults = null;
        analysisResult = string.Empty;
    }

    private async Task CheckStyle()
    {
        if (string.IsNullOrWhiteSpace(editorContent)) return;

        isChecking = true;
        statusText = Loc["editor.status.analyzing_style"];
        checkProgress = 50;
        ClearResults();

        try
        {
        }
        catch (Exception ex)
        {
            analysisResult = $"{Loc["common.error"]} {ex.Message}";
        }
        finally
        {
            statusText = Loc["common.status.ready"];
            isChecking = false;
            checkProgress = 0;
        }
    }

    private async Task SpellCheck()
    {
        if (string.IsNullOrWhiteSpace(editorContent)) return;

        isChecking = true;
        statusText = Loc["editor.status.checking_spelling"];
        checkProgress = 50;
        ClearResults();

        try
        {
            originalText = editorContent;
            correctedText = "";
            OpState.SetStatus(Loc["editor.status.checking_spelling"], true);
            OpState.SetThought("");

            await OllamaService.ChatStream(OllamaSettings.SpellingModel, editorContent, async chunk =>
            {
                correctedText += chunk;
                OpState.AppendThought(chunk);
                await InvokeAsync(StateHasChanged);
            }, OllamaSettings.SpellingPrompt, OllamaSettings.SpellingTemp);

            showSpellDiff = true;
            OpState.SetStatus(Loc["common.status.ready"], false);
        }
        catch (Exception ex)
        {
            analysisResult = $"{Loc["common.error"]} {ex.Message}";
            OpState.SetStatus($"{Loc["common.error"]} {ex.Message}", false);
        }
        finally
        {
            OpState.Reset();
            statusText = Loc["common.status.ready"];
            isChecking = false;
            checkProgress = 0;
        }
    }

    private void ApplySpellCorrections()
    {
        if (string.IsNullOrEmpty(correctedText)) return;
        editorContent = correctedText;
        _contentModified = editorContent != _lastSavedContent;
        showSpellDiff = false;
        ResetAutosaveTimer();
        statusText = Loc["editor.status.corrections_applied"];
    }

    private async Task RagSearch()
    {
        if (string.IsNullOrWhiteSpace(searchQuery)) return;

        isChecking = true;
        statusText = Loc["editor.status.searching_rag"];
        ClearResults();
        analysisResult = string.Empty;

        try
        {
            if (currentProjectId == 0)
            {
                analysisResult = Loc["editor.rag.error.no_project"];
                return;
            }

            ragResults = await RagService.SearchAsync(currentProjectId, searchQuery, 5);

            if (ragResults.Count == 0)
            {
                analysisResult = Loc["editor.rag.error.no_facts"];
            }
        }
        catch (Exception ex)
        {
            analysisResult = $"{Loc["editor.rag.error.generic"]} {ex.Message}";
        }
        finally
        {
            statusText = Loc["common.status.ready"];
            isChecking = false;
        }
    }

    private async Task SearchWikipedia()
    {
        if (string.IsNullOrWhiteSpace(searchQuery)) return;

        isChecking = true;
        statusText = Loc["editor.status.searching_wikipedia"];
        ClearResults();
        analysisResult = string.Empty;
        loadedArticles.Clear();

        try
        {
            var request = new WikipediaSearchRequest
            {
                Query = searchQuery,
                SearchGerman = true,
                SearchEnglish = true,
                MaxResults = 3
            };

            wikipediaResults = await WikipediaService.SearchAsync(request);

            foreach (var result in wikipediaResults)
            {
                var extract = await WikipediaService.GetExtractAsync(result.Title, result.Language);
                if (extract != null)
                    result.Extract = extract.Length > 800 ? extract[..800] + "..." : extract;
            }

            if (wikipediaResults.Count == 0)
                analysisResult = Loc["editor.wikipedia.error.not_found"];
        }
        catch (Exception ex)
        {
            analysisResult = $"{Loc["editor.wikipedia.error.generic"]} {ex.Message}";
        }
        finally
        {
            statusText = Loc["common.status.ready"];
            isChecking = false;
        }
    }

    private async Task LoadFullArticle(WikipediaResult result)
    {
        statusText = $"{Loc["editor.status.loading_article"]} {result.Title}...";

        try
        {
            var fullText = await WikipediaService.GetFullArticleAsync(result.Title, result.Language);
            if (fullText != null)
            {
                result.Extract = fullText;
                loadedArticles[result.Url] = true;
                statusText = Loc["editor.wikipedia.status.loaded"];
            }
            else
            {
                statusText = Loc["editor.wikipedia.error.load_failed"];
            }
        }
        catch (Exception ex)
        {
            statusText = $"{Loc["common.error"]} {ex.Message}";
        }
    }

    private async Task SaveAsFact(WikipediaResult result)
    {
        if (string.IsNullOrWhiteSpace(result.Extract) || currentProjectId == 0) return;

        statusText = Loc["editor.status.saving_fact"];

        try
        {
            var projects = await ProjectService.GetAllProjectsAsync();
            var project = projects.FirstOrDefault(p => p.Id == currentProjectId);
            if (project == null)
            {
                statusText = Loc["editor.error.project_not_found"];
                return;
            }

            var factsDir = Path.Combine(project.RootPath, "01_Fakten");
            Directory.CreateDirectory(factsDir);

            var safeName = SanitizeFilename(searchQuery);
            var filePath = Path.Combine(factsDir, $"{safeName}.md");

            var content = $"# {result.Title}\n" +
                          $"- Quelle: [{result.Title}]({result.Url})\n" +
                          $"- Sprache: {result.Language.ToUpper()}\n" +
                          $"- Abgerufen: {DateTime.Now:dd.MM.yyyy HH:mm}\n\n" +
                          result.Extract;

            await File.WriteAllTextAsync(filePath, content);
            statusText = $"{Loc["editor.status.saved_as"]} {safeName}.md";
        }
        catch (Exception ex)
        {
            statusText = $"{Loc["editor.error.save_failed"]} {ex.Message}";
        }
    }

    private static string SanitizeFilename(string name)
    {
        var invalid = Path.GetInvalidFileNameChars();
        var sanitized = new string(name.Where(c => !invalid.Contains(c)).ToArray());
        return string.IsNullOrWhiteSpace(sanitized) ? "fakt" : sanitized.Trim();
    }

    private void CancelCreateProject()
    {
        showCreateDialog = false;
        Navigation.NavigateTo("/editor");
    }

    private async Task CreateProject()
    {
        if (string.IsNullOrWhiteSpace(newProjectName) || string.IsNullOrWhiteSpace(newProjectPath)) return;

        if (!ProjectPathHelper.IsPathAllowed(newProjectPath))
        {
            showCreateDialog = false;
            statusText = $"⚠ Pfad nicht erlaubt. Verwende: {ProjectPathHelper.AllowedBasePath}";
            return;
        }

        isChecking = true;
        statusText = Loc["editor.status.creating_project"];

        try
        {
            Directory.CreateDirectory(newProjectPath);
            var userId = AuthService.CurrentUser?.Id ?? 0;
            var id = await ProjectService.CreateNewProjectAsync(newProjectName, newProjectPath, userId, selectedTemplate);
            SettingsService.CurrentProjectId = id;
            await SettingsService.LoadProjectSettingsAsync();
            showCreateDialog = false;
            Navigation.NavigateTo($"/editor/index/{id}");
        }
        catch (Exception ex)
        {
            analysisResult = $"{Loc["editor.error.create_failed"]} {ex.Message}";
        }
        finally
        {
            isChecking = false;
            statusText = Loc["common.status.ready"];
        }
    }

    private async Task IndexProject()
    {
        if (currentProjectId == 0)
        {
            analysisResult = Loc["editor.index.error.no_project"];
            return;
        }

        isChecking = true;
        statusText = Loc["editor.status.indexing"];
        analysisResult = string.Empty;
        ragResults = null;

        try
        {
            var projects = await ProjectService.GetAllProjectsAsync();
            var project = projects.FirstOrDefault(p => p.Id == currentProjectId);
            if (project == null)
            {
                analysisResult = Loc["editor.index.error.not_found"];
                return;
            }

            var p = new Progress<string>(msg =>
            {
                statusText = msg;
                InvokeAsync(StateHasChanged);
            });

            await RagService.IndexProjectAsync(currentProjectId, project.RootPath, p);
            analysisResult = Loc["editor.index.status.success"];
        }
        catch (Exception ex)
        {
            analysisResult = $"{Loc["editor.index.error.generic"]} {ex.Message}";
        }
        finally
        {
            statusText = Loc["common.status.ready"];
            isChecking = false;
        }
    }

    private async Task SaveCurrentFile()
    {
        if (string.IsNullOrEmpty(currentFile)) return;
        await File.WriteAllTextAsync(currentFile, editorContent);
        _lastSavedContent = editorContent;
        _contentModified = false;
        statusText = $"{Loc["editor.status.saved"]} {Path.GetFileName(currentFile)}";
    }

    private void ShowVersionDialog()
    {
        showVersionDialog = true;
        versionComment = string.Empty;
    }

    private async Task SaveVersioned()
    {
        if (string.IsNullOrEmpty(currentFile)) return;

        var dir = Path.GetDirectoryName(currentFile)!;
        var name = Path.GetFileNameWithoutExtension(currentFile);
        var ext = Path.GetExtension(currentFile);
        var versionsDir = Path.Combine(dir, "_versions");
        Directory.CreateDirectory(versionsDir);

        var stamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
        var versionPath = Path.Combine(versionsDir, $"{name}_{stamp}{ext}");

        await File.WriteAllTextAsync(currentFile, editorContent);
        await File.WriteAllTextAsync(versionPath, editorContent);
        var meta = $"Kommentar: {versionComment}";
        await File.WriteAllTextAsync(versionPath + ".meta", meta);

        statusText = $"{Loc["editor.status.version_saved"]} {name}_{stamp}{ext}";
        showVersionDialog = false;
        versionComment = string.Empty;
        _lastSavedContent = editorContent;
        _contentModified = false;
        RefreshLatestVersion();
    }

    private void ShowVersionBrowser()
    {
        ClearResults();
        LoadVersionList();
        showVersionBrowser = true;
    }

    private void CloseVersionBrowser()
    {
        showVersionBrowser = false;
        showVersionDiff = false;
        _selectedVersion = null;
    }

    private void LoadVersionList()
    {
        _versionList.Clear();
        if (string.IsNullOrEmpty(currentFile)) return;

        var dir = Path.GetDirectoryName(currentFile)!;
        var name = Path.GetFileNameWithoutExtension(currentFile);
        var ext = Path.GetExtension(currentFile);
        var versionsDir = Path.Combine(dir, "_versions");
        if (!Directory.Exists(versionsDir)) return;

        var pattern = $"{name}_*{ext}";
        var files = Directory.GetFiles(versionsDir, pattern).OrderByDescending(f => f).ToList();

        foreach (var file in files)
        {
            var metaFile = file + ".meta";
            string? comment = null;
            if (File.Exists(metaFile))
            {
                var lines = File.ReadAllLines(metaFile);
                if (lines.Length > 0 && lines[0].StartsWith("Kommentar: "))
                    comment = lines[0]["Kommentar: ".Length..];
            }

            var fileName = Path.GetFileNameWithoutExtension(file);
            var stamp = fileName[(name.Length + 1)..];
            var parsed = DateTime.TryParseExact(stamp, "yyyyMMdd_HHmmss", null,
                System.Globalization.DateTimeStyles.None, out var dt);

            _versionList.Add(new VersionEntry(
                file,
                parsed ? dt : File.GetLastWriteTime(file),
                comment
            ));
        }
    }

    private async void ShowVersionDiff(VersionEntry version)
    {
        try
        {
            _versionDiffContent = await File.ReadAllTextAsync(version.FilePath);
            _selectedVersion = version;
            showVersionDiff = true;
        }
        catch (Exception ex)
        {
            statusText = $"{Loc["common.error"]} {ex.Message}";
        }
    }

    private async void RestoreVersion(VersionEntry version)
    {
        try
        {
            editorContent = await File.ReadAllTextAsync(version.FilePath);
            _contentModified = editorContent != _lastSavedContent;
            ResetAutosaveTimer();
            statusText = $"{Loc["editor.version.status_loaded_prefix"]} {version.Timestamp:dd.MM.yyyy HH:mm} {Loc["editor.version.status_loaded_suffix"]}";
            showVersionBrowser = false;
            showVersionDiff = false;
            _selectedVersion = null;
        }
        catch (Exception ex)
        {
            statusText = $"{Loc["common.error"]} {ex.Message}";
        }
    }

    private async Task WrapSelection(string prefix, string suffix)
        => await JS.InvokeVoidAsync("EditorToolbar.wrapSelection", prefix, suffix);

    private async Task InsertHeading(int level)
        => await JS.InvokeVoidAsync("EditorToolbar.insertHeading", level);

    private async Task InsertList(bool ordered)
        => await JS.InvokeVoidAsync("EditorToolbar.insertList", ordered);

    private async Task IndentLines()
        => await JS.InvokeVoidAsync("EditorToolbar.indentLines");

    private async Task OutdentLines()
        => await JS.InvokeVoidAsync("EditorToolbar.outdentLines");

    private void RefreshLatestVersion()
    {
        latestVersion = null;
        if (string.IsNullOrEmpty(currentFile)) return;

        var dir = Path.GetDirectoryName(currentFile)!;
        var name = Path.GetFileNameWithoutExtension(currentFile);
        var ext = Path.GetExtension(currentFile);
        var versionsDir = Path.Combine(dir, "_versions");
        if (!Directory.Exists(versionsDir)) return;

        var pattern = $"{name}_*{ext}";
        var files = Directory.GetFiles(versionsDir, pattern).OrderByDescending(f => f).ToList();
        if (files.Count == 0) return;

        var latest = files.First();
        var metaFile = latest + ".meta";
        string? comment = null;
        if (File.Exists(metaFile))
        {
            var lines = File.ReadAllLines(metaFile);
            if (lines.Length > 0 && lines[0].StartsWith("Kommentar: "))
                comment = lines[0]["Kommentar: ".Length..];
        }

        var fileName = Path.GetFileNameWithoutExtension(latest);
        var stamp = fileName[(name.Length + 1)..];
        var parsed = DateTime.TryParseExact(stamp, "yyyyMMdd_HHmmss", null,
            System.Globalization.DateTimeStyles.None, out var dt);
        latestVersion = new VersionEntry(
            latest,
            parsed ? dt : File.GetLastWriteTime(latest),
            comment
        );
    }
}
