@model ChapterStoryIntelligenceDryRunViewModel
@{
    ViewData["Title"] = "Chapter Story Intelligence Test";
    var result = Model.Result;
    var canSave = result is not null && !result.PipelineStopped && string.IsNullOrWhiteSpace(result.ErrorMessage);
    var savePayload = canSave
        ? System.Text.Json.JsonSerializer.Serialize(
            Model,
            new System.Text.Json.JsonSerializerOptions
            {
                PropertyNamingPolicy = System.Text.Json.JsonNamingPolicy.CamelCase,
                WriteIndented = false
            })
        : string.Empty;
}

<div class="page-heading compact">
    <div>
        <p class="eyebrow">Admin utility</p>
        <h1>Chapter Story Intelligence Test</h1>
        <p class="lead-text">This dry run detects suggested scene boundaries, runs Scene Intelligence sequentially, and can save the resulting audit data only when you choose to save it.</p>
    </div>
</div>

<nav class="mb-3" aria-label="Breadcrumb">
    <a asp-controller="Projects" asp-action="Index">Projects</a>
    <span class="muted"> / </span>
    <a asp-action="Index">Admin</a>
    <span class="muted"> / </span>
    <a asp-action="StoryIntelligenceDiagnostics">Story Intelligence Diagnostics</a>
    <span class="muted"> / Chapter Story Intelligence Test</span>
</nav>

<section class="edit-panel">
    <h2>Manual combined pipeline test</h2>
    <p>Use fictional sample text only. This page runs Chapter Structure first, splits the submitted chapter into in-memory scene blocks, then runs Scene Intelligence for each block without creating PlotDirector scenes.</p>
    <form asp-action="ChapterStoryIntelligenceTest" method="post">
        <div class="row g-3">
            <div class="col-md-3">
                <label class="form-label" asp-for="Form.ProjectID">ProjectID</label>
                <input class="form-control" asp-for="Form.ProjectID" />
            </div>
            <div class="col-md-3">
                <label class="form-label" asp-for="Form.BookID">BookID</label>
                <input class="form-control" asp-for="Form.BookID" />
            </div>
            <div class="col-md-3">
                <label class="form-label" asp-for="Form.ChapterID">ChapterID</label>
                <input class="form-control" asp-for="Form.ChapterID" />
            </div>
            <div class="col-md-3">
                <label class="form-label" asp-for="Form.ChapterNumber">ChapterNumber</label>
                <input class="form-control" asp-for="Form.ChapterNumber" />
            </div>
            <div class="col-12">
                <label class="form-label" asp-for="Form.SourceLabel">SourceLabel</label>
                <input class="form-control" asp-for="Form.SourceLabel" />
            </div>
            <div class="col-12">
                <label class="form-label" asp-for="Form.ChapterText">Chapter text</label>
                <textarea class="form-control" asp-for="Form.ChapterText" rows="14"></textarea>
            </div>
        </div>
        <div class="button-row mt-3">
            <button class="btn btn-primary" type="submit">Run combined test</button>
            <button class="btn btn-outline-primary" type="submit" formaction="@Url.Action("QueueChapterStoryIntelligenceRun")">Queue persisted run</button>
            <a class="btn btn-outline-secondary" asp-action="StoryIntelligenceDiagnostics">Back to diagnostics</a>
        </div>
    </form>
</section>

@if (result is not null)
{
    <section class="edit-panel">
        <h2>Combined pipeline result</h2>
        @if (!string.IsNullOrWhiteSpace(result.ErrorMessage))
        {
            <div class="alert alert-warning">@result.ErrorMessage</div>
        }
        @if (result.PipelineStopped)
        {
            <div class="alert alert-warning">@result.StopReason</div>
        }

        <div class="row g-3">
            <div class="col-md-3">
                <div class="border rounded p-3 h-100">
                    <p class="eyebrow">Model</p>
                    <h3 class="mb-0">@Display(result.Model)</h3>
                </div>
            </div>
            <div class="col-md-3">
                <div class="border rounded p-3 h-100">
                    <p class="eyebrow">Paragraphs</p>
                    <h3 class="mb-0">@result.ParagraphCount.ToString("N0")</h3>
                </div>
            </div>
            <div class="col-md-3">
                <div class="border rounded p-3 h-100">
                    <p class="eyebrow">Detected scenes</p>
                    <h3 class="mb-0">@result.SceneBlocks.Count.ToString("N0")</h3>
                </div>
            </div>
            <div class="col-md-3">
                <div class="border rounded p-3 h-100">
                    <p class="eyebrow">Total duration</p>
                    <h3 class="mb-0">@Display(result.TotalDuration)</h3>
                </div>
            </div>
        </div>

        <dl class="mt-3 mb-0">
            <dt>Chapter prompt</dt>
            <dd>@result.ChapterPromptVersion</dd>
            <dt>Scene prompt</dt>
            <dd>@result.ScenePromptVersion</dd>
            <dt>Total input tokens</dt>
            <dd>@Display(result.TotalInputTokens)</dd>
            <dt>Total output tokens</dt>
            <dd>@Display(result.TotalOutputTokens)</dd>
        </dl>

        @if (canSave)
        {
            <form asp-action="SaveChapterStoryIntelligenceRun" method="post" class="mt-3">
                <textarea name="Payload" class="d-none" aria-hidden="true">@savePayload</textarea>
                <div class="button-row">
                    <button class="btn btn-primary" type="submit">Save this run</button>
                    <a class="btn btn-outline-secondary" asp-action="StoryIntelligenceRuns">View saved runs</a>
                </div>
                <p class="muted mt-2 mb-0">Saving creates a new audit record each time. It stores raw and parsed AI output plus validation counts, and does not create PlotDirector scenes or story entities.</p>
            </form>
        }
        else
        {
            <div class="alert alert-warning mt-3 mb-0">This run cannot be saved because the combined pipeline did not complete.</div>
        }
    </section>

    <section class="edit-panel">
        <h2>Chapter Structure</h2>
        <div class="row g-3">
            <div class="col-md-4">
                <div class="border rounded p-3 h-100">
                    <p class="eyebrow">Duration</p>
                    <h3 class="mb-0">@Display(result.ChapterDuration)</h3>
                </div>
            </div>
            <div class="col-md-4">
                <div class="border rounded p-3 h-100">
                    <p class="eyebrow">Input tokens</p>
                    <h3 class="mb-0">@Display(result.ChapterInputTokens)</h3>
                </div>
            </div>
            <div class="col-md-4">
                <div class="border rounded p-3 h-100">
                    <p class="eyebrow">Output tokens</p>
                    <h3 class="mb-0">@Display(result.ChapterOutputTokens)</h3>
                </div>
            </div>
        </div>

        <h3 class="mt-3">Validation</h3>
        @if (result.ChapterValidation is null)
        {
            <div class="alert alert-warning">Chapter Structure validation did not run.</div>
        }
        else
        {
            <h4>@ValidationSummary(result.ChapterValidation)</h4>
            @RenderIssues("Errors", result.ChapterValidation.Errors)
            @RenderIssues("Warnings", result.ChapterValidation.Warnings)
            @RenderIssues("Information", result.ChapterValidation.Information)
        }

        <h3 class="mt-3">Parsed Model</h3>
        @if (result.ParsedChapter is null)
        {
            <p>No parsed Chapter Structure model is available.</p>
        }
        else
        {
            <p>@Display(result.ParsedChapter.ChapterSummary)</p>
            @if (result.ParsedChapter.SceneBoundaries is not null)
            {
                <ol>
                    @foreach (var boundary in result.ParsedChapter.SceneBoundaries)
                    {
                        <li>Scene @Display(boundary.SceneNumber): paragraphs @Display(boundary.StartParagraph)-@Display(boundary.EndParagraph), confidence @Display(boundary.Confidence). @Display(boundary.Reason)</li>
                    }
                </ol>
            }
        }

        <h3 class="mt-3">Raw JSON</h3>
        <label class="form-label" for="chapterStructureJson">Extracted Chapter Structure JSON</label>
        <textarea class="form-control font-monospace" id="chapterStructureJson" rows="12" readonly>@result.ChapterJsonText</textarea>
        <label class="form-label mt-3" for="chapterRawResponse">Raw OpenAI chapter response</label>
        <textarea class="form-control font-monospace" id="chapterRawResponse" rows="12" readonly>@result.ChapterRawResponseText</textarea>
    </section>

    <section class="edit-panel">
        <h2>Detected Scene List</h2>
        @if (result.SceneBlocks.Count == 0)
        {
            <p>No in-memory scene blocks were produced.</p>
        }
        else
        {
            <ol class="mb-0">
                @foreach (var block in result.SceneBlocks)
                {
                    <li>
                        Scene @Display(block.TemporarySceneNumber), paragraphs @Display(block.StartParagraph)-@Display(block.EndParagraph):
                        @Display(block.SceneTextPreview)
                    </li>
                }
            </ol>
        }
    </section>

    @foreach (var block in result.SceneBlocks)
    {
        <section class="edit-panel">
            <h2>Suggested Scene @Display(block.TemporarySceneNumber)</h2>
            <dl>
                <dt>Paragraph range</dt>
                <dd>@Display(block.StartParagraph)-@Display(block.EndParagraph)</dd>
                <dt>Boundary confidence</dt>
                <dd>@Display(block.BoundaryConfidence)</dd>
                <dt>Boundary reason</dt>
                <dd>@Display(block.BoundaryReason)</dd>
                <dt>Scene Intelligence duration</dt>
                <dd>@Display(block.Duration)</dd>
                <dt>Input tokens</dt>
                <dd>@Display(block.InputTokens)</dd>
                <dt>Output tokens</dt>
                <dd>@Display(block.OutputTokens)</dd>
            </dl>

            @if (!block.SplitValid)
            {
                <div class="alert alert-warning">@block.SplitErrorMessage</div>
            }
            @if (!string.IsNullOrWhiteSpace(block.ErrorMessage))
            {
                <div class="alert alert-warning">@block.ErrorMessage</div>
            }

            <h3>Scene Text Preview</h3>
            <p>@Display(block.SceneTextPreview)</p>

            <h3>Validation</h3>
            @if (block.Validation is null)
            {
                <p>Scene validation did not run.</p>
            }
            else
            {
                <h4>@ValidationSummary(block.Validation)</h4>
                @RenderIssues("Errors", block.Validation.Errors)
                @RenderIssues("Warnings", block.Validation.Warnings)
            }

            <h3>Parsed Model</h3>
            @if (block.ParsedScene is null)
            {
                <p>No parsed Scene Intelligence model is available.</p>
            }
            else
            {
                <dl>
                    <dt>Summary</dt>
                    <dd>@Display(block.ParsedScene.Summary?.Short)</dd>
                    <dt>Setting</dt>
                    <dd>@Display(block.ParsedScene.Setting?.LocationName) (@Display(block.ParsedScene.Setting?.LocationType))</dd>
                    <dt>Characters</dt>
                    <dd>@Display(block.ParsedScene.Characters?.Count)</dd>
                    <dt>Locations</dt>
                    <dd>@Display(block.ParsedScene.Locations?.Count)</dd>
                    <dt>Assets</dt>
                    <dd>@Display(block.ParsedScene.Assets?.Count)</dd>
                    <dt>Observations</dt>
                    <dd>@Display(block.ParsedScene.Observations?.Count)</dd>
                </dl>
            }

            <h3>Raw Scene Intelligence Response</h3>
            <label class="form-label" for="sceneJson-@block.TemporarySceneNumber">Extracted Scene Intelligence JSON</label>
            <textarea class="form-control font-monospace" id="sceneJson-@block.TemporarySceneNumber" rows="12" readonly>@block.SceneJsonText</textarea>
            <label class="form-label mt-3" for="rawScene-@block.TemporarySceneNumber">Raw OpenAI response</label>
            <textarea class="form-control font-monospace" id="rawScene-@block.TemporarySceneNumber" rows="12" readonly>@block.RawResponseText</textarea>
        </section>
    }
}

@functions {
    private static string Display(object? value) => value switch
    {
        null => "None",
        string text when string.IsNullOrWhiteSpace(text) => "None",
        decimal number => number.ToString("0.##"),
        bool flag => flag ? "Yes" : "No",
        int number => number.ToString("N0"),
        _ => value.ToString() ?? "None"
    };

    private static string ValidationSummary(PlotLine.Models.StoryIntelligence.ValidationResult validation)
        => validation.IsValid
            ? $"✓ Passed with {validation.Warnings.Count:N0} warning(s)"
            : $"✖ Failed with {validation.Errors.Count:N0} error(s) and {validation.Warnings.Count:N0} warning(s)";

    private static string SeveritySymbol(string severity) => severity switch
    {
        "Error" => "✖",
        "Warning" => "⚠",
        _ => "✓"
    };

    private static Microsoft.AspNetCore.Html.IHtmlContent RenderIssues(string heading, IReadOnlyList<PlotLine.Models.StoryIntelligence.ValidationIssue> issues)
    {
        var builder = new System.Text.StringBuilder();
        builder.Append("<section class=\"mt-3\">");
        builder.Append("<h4>").Append(Encode(heading)).Append(" (").Append(issues.Count.ToString("N0")).Append(")</h4>");
        if (issues.Count == 0)
        {
            builder.Append("<p class=\"mb-0\">None.</p>");
        }
        else
        {
            builder.Append("<div class=\"list-group\">");
            foreach (var issue in issues)
            {
                builder.Append("<div class=\"list-group-item\">");
                builder.Append("<p class=\"mb-1\"><strong>")
                    .Append(Encode(SeveritySymbol(issue.Severity)))
                    .Append(' ')
                    .Append(Encode(issue.Severity))
                    .Append("</strong> <code>")
                    .Append(Encode(issue.Path))
                    .Append("</code></p>");
                builder.Append("<p class=\"mb-1\">").Append(Encode(issue.Message)).Append("</p>");
                builder.Append("<p class=\"mb-0 muted\">").Append(Encode(issue.SuggestedFix)).Append("</p>");
                builder.Append("</div>");
            }

            builder.Append("</div>");
        }

        builder.Append("</section>");
        return new Microsoft.AspNetCore.Html.HtmlString(builder.ToString());
    }

    private static string Encode(string? value)
        => System.Net.WebUtility.HtmlEncode(value ?? string.Empty);
}
