using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using PlotLine.Models;
using PlotLine.Services;
using PlotLine.ViewModels;

namespace PlotLine.Controllers;

[Authorize(Policy = "AdminOnly")]
[Route("admin")]
public sealed class AdminController(
    ICurrentUserService currentUser,
    IFeatureRequestService featureRequests,
    IEmailService emails,
    IStoryIntelligenceDiagnosticsService storyIntelligenceDiagnostics,
    IStoryIntelligenceDryRunService storyIntelligenceDryRun,
    IChapterStructureDryRunService chapterStructureDryRun,
    IChapterStoryIntelligenceDryRunService chapterStoryIntelligenceDryRun,
    IStoryIntelligenceResultPersistenceService storyIntelligenceResults,
    IStoryIntelligenceExistingChapterQueueService existingChapterQueue,
    IStoryIntelligenceFullChapterTestService fullChapterTest,
    IStoryIntelligenceImportCommitService storyIntelligenceImportCommit,
    IIllustrationLibraryService illustrationLibrary) : Controller
{
    [HttpGet("")]
    [HttpGet("index")]
    public async Task<IActionResult> Index()
    {
        return View(new AdminDashboardViewModel
        {
            FeatureRequestSummary = await featureRequests.GetAdminSummaryAsync()
        });
    }

    [HttpGet("theme-audit")]
    public IActionResult ThemeAudit()
    {
        return View();
    }

    [HttpGet("story-intelligence/illustration-library")]
    public async Task<IActionResult> StoryIntelligenceIllustrationLibrary([FromQuery] IllustrationLibraryFilter filter)
    {
        return View(await illustrationLibrary.BuildAdminViewModelAsync(filter));
    }

    [HttpPost("story-intelligence/illustration-library/starter-plan")]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> CreateIllustrationStarterPlan()
    {
        var result = await illustrationLibrary.EnsureStarterPlanAsync();
        TempData[result.Succeeded ? "AdminMessage" : "AdminError"] = result.Message;
        return RedirectToAction(nameof(StoryIntelligenceIllustrationLibrary));
    }

    [HttpPost("story-intelligence/illustration-library/generate-starter")]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> GenerateIllustrationStarterLibrary(IllustrationStarterGenerationForm form)
    {
        var result = await illustrationLibrary.GenerateStarterLibraryAsync(form.Confirmed);
        TempData[result.Succeeded ? "AdminMessage" : "AdminError"] = result.Message;
        return RedirectToAction(nameof(StoryIntelligenceIllustrationLibrary));
    }

    [HttpPost("story-intelligence/illustration-library/queue")]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> QueueIllustrationLibraryItems(IllustrationLibraryQueueForm form)
    {
        var result = await illustrationLibrary.QueueAsync(null, form.Category, form.Confirmed);
        TempData[result.Succeeded ? "AdminMessage" : "AdminError"] = result.Message;
        return RedirectToAction(nameof(StoryIntelligenceIllustrationLibrary), new { category = form.Category });
    }

    [HttpPost("story-intelligence/illustration-library/requeue-failed")]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> RequeueFailedIllustrationLibraryItems(IllustrationLibraryBulkRetryForm form)
    {
        var result = await illustrationLibrary.RequeueAllFailedAsync();
        TempData[result.QueuedCount > 0 ? "AdminMessage" : "AdminError"] = result.Message;
        return RedirectToAction(nameof(StoryIntelligenceIllustrationLibrary), new { category = form.Category, status = form.Status, search = form.Search });
    }

    [HttpPost("story-intelligence/illustration-library/{id:int}/queue")]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> QueueIllustrationLibraryItem(int id, IllustrationLibraryQueueForm form)
    {
        var result = await illustrationLibrary.QueueAsync(id, null, form.Confirmed);
        TempData[result.Succeeded ? "AdminMessage" : "AdminError"] = result.Message;
        return RedirectToAction(nameof(StoryIntelligenceIllustrationLibrary), PreserveIllustrationLibraryFilter(form));
    }

    [HttpPost("story-intelligence/illustration-library/{id:int}/approve")]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> ApproveIllustrationLibraryItem(int id, IllustrationLibraryQueueForm form)
    {
        if (currentUser.UserId is not int userId)
        {
            return Forbid();
        }

        var result = await illustrationLibrary.ApproveAsync(id, userId);
        TempData[result.Succeeded ? "AdminMessage" : "AdminError"] = result.Message;
        return RedirectToAction(nameof(StoryIntelligenceIllustrationLibrary), PreserveIllustrationLibraryFilter(form));
    }

    [HttpPost("story-intelligence/illustration-library/{id:int}/reject")]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> RejectIllustrationLibraryItem(int id, IllustrationLibraryRejectForm form)
    {
        if (currentUser.UserId is not int userId)
        {
            return Forbid();
        }

        var result = await illustrationLibrary.RejectAsync(id, userId, form.Reason);
        TempData[result.Succeeded ? "AdminMessage" : "AdminError"] = result.Message;
        return RedirectToAction(nameof(StoryIntelligenceIllustrationLibrary), PreserveIllustrationLibraryFilter(form));
    }

    [HttpPost("story-intelligence/illustration-library/{id:int}/regenerate")]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> RegenerateIllustrationLibraryItem(int id, IllustrationLibraryQueueForm form)
    {
        var result = await illustrationLibrary.RegenerateAsync(id, form.Confirmed);
        TempData[result.Succeeded ? "AdminMessage" : "AdminError"] = result.Message;
        return RedirectToAction(nameof(StoryIntelligenceIllustrationLibrary), PreserveIllustrationLibraryFilter(form));
    }

    [HttpPost("story-intelligence/illustration-library/{id:int}/disable")]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> DisableIllustrationLibraryItem(int id, IllustrationLibraryQueueForm form)
    {
        var result = await illustrationLibrary.DisableAsync(id);
        TempData[result.Succeeded ? "AdminMessage" : "AdminError"] = result.Message;
        return RedirectToAction(nameof(StoryIntelligenceIllustrationLibrary), PreserveIllustrationLibraryFilter(form));
    }

    [HttpPost("story-intelligence/illustration-library/{id:int}/metadata")]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> UpdateIllustrationLibraryMetadata(int id, IllustrationLibraryMetadataForm form)
    {
        var result = await illustrationLibrary.UpdateMetadataAsync(id, form);
        TempData[result.Succeeded ? "AdminMessage" : "AdminError"] = result.Message;
        return RedirectToAction(nameof(StoryIntelligenceIllustrationLibrary), PreserveIllustrationLibraryFilter(form));
    }

    private static object PreserveIllustrationLibraryFilter(IllustrationLibraryQueueForm form)
        => new { category = form.Category, status = form.Status, search = form.Search };

    private static object PreserveIllustrationLibraryFilter(IllustrationLibraryRejectForm form)
        => new { category = form.Category, status = form.Status, search = form.Search };

    private static object PreserveIllustrationLibraryFilter(IllustrationLibraryMetadataForm form)
        => new { category = form.Category, status = form.Status, search = form.Search };

    [HttpGet("story-intelligence-diagnostics")]
    public async Task<IActionResult> StoryIntelligenceDiagnostics(CancellationToken cancellationToken)
    {
        return View(await storyIntelligenceDiagnostics.GetDiagnosticsAsync(cancellationToken));
    }

    [HttpGet("story-intelligence-test")]
    public IActionResult StoryIntelligenceTest()
    {
        return View(storyIntelligenceDryRun.GetDefault());
    }

    [HttpPost("story-intelligence-test")]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> StoryIntelligenceTest([Bind(Prefix = "Form")] StoryIntelligenceDryRunForm form, CancellationToken cancellationToken)
    {
        return View(await storyIntelligenceDryRun.ExecuteAsync(form, cancellationToken));
    }

    [HttpGet("chapter-structure-test")]
    public IActionResult ChapterStructureTest()
    {
        return View(chapterStructureDryRun.GetDefault());
    }

    [HttpPost("chapter-structure-test")]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> ChapterStructureTest([Bind(Prefix = "Form")] ChapterStructureDryRunForm form, CancellationToken cancellationToken)
    {
        return View(await chapterStructureDryRun.ExecuteAsync(form, cancellationToken));
    }

    [HttpGet("chapter-story-intelligence-test")]
    public IActionResult ChapterStoryIntelligenceTest()
    {
        return View(chapterStoryIntelligenceDryRun.GetDefault());
    }

    [HttpPost("chapter-story-intelligence-test")]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> ChapterStoryIntelligenceTest([Bind(Prefix = "Form")] ChapterStoryIntelligenceDryRunForm form, CancellationToken cancellationToken)
    {
        return View(await chapterStoryIntelligenceDryRun.ExecuteAsync(form, cancellationToken));
    }

    [HttpPost("chapter-story-intelligence-test/save")]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> SaveChapterStoryIntelligenceRun(StoryIntelligenceSaveRunForm form)
    {
        if (currentUser.UserId is not int userId)
        {
            return Forbid();
        }

        var runId = await storyIntelligenceResults.SaveDisplayedRunAsync(userId, form.Payload);
        TempData["AdminMessage"] = $"Story Intelligence run {runId:N0} saved. Saving the same displayed dry run again creates a separate audit record.";
        return RedirectToAction(nameof(StoryIntelligenceRunDetails), new { id = runId });
    }

    [HttpPost("chapter-story-intelligence-test/queue")]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> QueueChapterStoryIntelligenceRun([Bind(Prefix = "Form")] ChapterStoryIntelligenceDryRunForm form)
    {
        if (currentUser.UserId is not int userId)
        {
            return Forbid();
        }

        var runId = await storyIntelligenceResults.QueueAdminTextRunAsync(userId, form);
        TempData["AdminMessage"] = $"Story Intelligence run {runId:N0} queued. The background worker will process it progressively.";
        return RedirectToAction(nameof(StoryIntelligenceRunDetails), new { id = runId });
    }

    [HttpGet("story-intelligence-existing-chapter")]
    public async Task<IActionResult> StoryIntelligenceExistingChapter([FromQuery] StoryIntelligenceExistingChapterQueueForm form)
    {
        return View(await existingChapterQueue.BuildViewModelAsync(form));
    }

    [HttpPost("story-intelligence-existing-chapter")]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> QueueStoryIntelligenceExistingChapter(StoryIntelligenceExistingChapterQueueForm form)
    {
        if (currentUser.UserId is not int userId)
        {
            return Forbid();
        }

        var model = await existingChapterQueue.QueueAsync(userId, form);
        if (model.QueuedRunID is int runId)
        {
            TempData["AdminMessage"] = $"Story Intelligence run {runId:N0} queued from existing chapter. The background worker will process it progressively.";
            return RedirectToAction(nameof(StoryIntelligenceRunDetails), new { id = runId });
        }

        return View(nameof(StoryIntelligenceExistingChapter), model);
    }

    [HttpGet("story-intelligence-full-chapter-test")]
    public async Task<IActionResult> StoryIntelligenceFullChapterTest(int? sourceRunId)
    {
        return View(await fullChapterTest.GetDefaultAsync(sourceRunId));
    }

    [HttpPost("story-intelligence-full-chapter-test")]
    [ValidateAntiForgeryToken]
    [RequestSizeLimit(5 * 1024 * 1024)]
    public async Task<IActionResult> StoryIntelligenceFullChapterTest(
        [Bind(Prefix = "Form")] StoryIntelligenceFullChapterTestForm form,
        IFormFile? textFile,
        string submitAction)
    {
        if (string.Equals(submitAction, "Queue", StringComparison.OrdinalIgnoreCase))
        {
            if (currentUser.UserId is not int userId)
            {
                return Forbid();
            }

            try
            {
                var runId = await fullChapterTest.QueueAsync(userId, form, textFile);
                TempData["AdminMessage"] = $"Full chapter Story Intelligence run {runId:N0} queued. The browser does not need to remain open.";
                return RedirectToAction(nameof(StoryIntelligenceRunDetails), new { id = runId });
            }
            catch (InvalidOperationException ex)
            {
                var model = await fullChapterTest.AnalyzeAsync(form, textFile);
                return View(new StoryIntelligenceFullChapterTestViewModel
                {
                    Form = model.Form,
                    Preflight = model.Preflight,
                    SourceFileName = model.SourceFileName,
                    SourceFileSizeBytes = model.SourceFileSizeBytes,
                    ErrorMessage = ex.Message
                });
            }
        }

        return View(await fullChapterTest.AnalyzeAsync(form, textFile));
    }

    [HttpGet("story-intelligence-runs")]
    public async Task<IActionResult> StoryIntelligenceRuns()
    {
        return View(await storyIntelligenceResults.ListRunsAsync());
    }

    [HttpGet("story-intelligence-runs/{id:int}")]
    public async Task<IActionResult> StoryIntelligenceRunDetails(int id)
    {
        var model = await storyIntelligenceResults.GetRunDetailAsync(id);
        return model is null ? NotFound() : View(model);
    }

    [HttpPost("story-intelligence-runs/{id:int}/cancel")]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> CancelStoryIntelligenceRun(int id)
    {
        await storyIntelligenceResults.CancelRunAsync(id);
        TempData["AdminMessage"] = $"Cancellation requested for Story Intelligence run {id:N0}.";
        return RedirectToAction(nameof(StoryIntelligenceRunDetails), new { id });
    }

    [HttpGet("story-intelligence-runs/{id:int}/commit")]
    public async Task<IActionResult> ConfirmStoryIntelligenceImportCommit(int id)
    {
        var model = await storyIntelligenceImportCommit.BuildConfirmationAsync(id);
        return model is null ? NotFound() : View(model);
    }

    [HttpPost("story-intelligence-runs/{id:int}/commit")]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> CommitStoryIntelligenceImport(int id)
    {
        if (currentUser.UserId is not int userId)
        {
            return Forbid();
        }

        try
        {
            var result = await storyIntelligenceImportCommit.CommitAsync(id, userId);
            TempData[result.Success ? "AdminMessage" : "AdminError"] = result.Message;
            return RedirectToAction(result.Success ? nameof(StoryIntelligenceRunDetails) : nameof(ConfirmStoryIntelligenceImportCommit), new { id });
        }
        catch (InvalidOperationException ex)
        {
            TempData["AdminError"] = ex.Message;
            return RedirectToAction(nameof(ConfirmStoryIntelligenceImportCommit), new { id });
        }
    }

    [HttpGet("feature-requests")]
    public async Task<IActionResult> FeatureRequests(string? status, string? appArea, string? importance, string? search, string? sort)
    {
        var filter = new AdminFeatureRequestFilter
        {
            Status = FeatureRequestStatuses.IsValid(status) ? status : null,
            AppArea = FeatureRequestOptions.AppAreas.Contains(appArea ?? string.Empty, StringComparer.OrdinalIgnoreCase) ? appArea : null,
            Importance = FeatureRequestOptions.ImportanceOptions.Contains(importance ?? string.Empty, StringComparer.OrdinalIgnoreCase) ? importance : null,
            Search = string.IsNullOrWhiteSpace(search) ? null : search.Trim(),
            Sort = FeatureRequestSortOptions.IsValid(sort) ? sort! : FeatureRequestSortOptions.LatestActivity
        };

        return View(new AdminFeatureRequestIndexViewModel
        {
            Status = filter.Status,
            AppArea = filter.AppArea,
            Importance = filter.Importance,
            Search = filter.Search,
            Sort = filter.Sort,
            Requests = await featureRequests.ListForAdminAsync(filter)
        });
    }

    [HttpGet("feature-requests/{id:int}")]
    public async Task<IActionResult> FeatureRequestDetails(int id)
    {
        var request = await featureRequests.GetForAdminAsync(id);
        if (request is null)
        {
            return NotFound();
        }

        return View(new FeatureRequestDetailsViewModel
        {
            IsAdminView = true,
            Request = request,
            Messages = await featureRequests.ListMessagesAsync(request.FeatureRequestID),
            Reply = new FeatureRequestReplyViewModel { FeatureRequestID = request.FeatureRequestID }
        });
    }

    [HttpPost("feature-requests/update")]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> UpdateFeatureRequest(AdminFeatureRequestUpdateViewModel model)
    {
        if (!FeatureRequestStatuses.IsValid(model.Status))
        {
            return BadRequest();
        }

        var before = await featureRequests.GetForAdminAsync(model.FeatureRequestID);
        if (before is null)
        {
            return NotFound();
        }

        var updated = await featureRequests.UpdateAdminFieldsAsync(model);
        if (!updated)
        {
            return NotFound();
        }

        var after = await featureRequests.GetForAdminAsync(model.FeatureRequestID);
        if (after is not null && !string.Equals(before.Status, after.Status, StringComparison.OrdinalIgnoreCase))
        {
            await emails.SendFeatureRequestStatusChangedAsync(
                after,
                BuildAbsoluteUrl("Details", "FeatureRequests", new { id = after.FeatureRequestID }),
                currentUser.Email ?? string.Empty);
        }

        TempData["AdminMessage"] = "Feature request updated.";
        return RedirectToAction(nameof(FeatureRequestDetails), new { id = model.FeatureRequestID });
    }

    [HttpPost("feature-requests/reply")]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> FeatureRequestReply([Bind(Prefix = "Reply")] FeatureRequestReplyViewModel model)
    {
        if (currentUser.UserId is not int userId)
        {
            return Forbid();
        }

        var request = await featureRequests.GetForAdminAsync(model.FeatureRequestID);
        if (request is null)
        {
            return NotFound();
        }

        if (!ModelState.IsValid)
        {
            return View(nameof(FeatureRequestDetails), new FeatureRequestDetailsViewModel
            {
                IsAdminView = true,
                Request = request,
                Messages = await featureRequests.ListMessagesAsync(request.FeatureRequestID),
                Reply = model
            });
        }

        await featureRequests.AddAdminReplyAsync(model.FeatureRequestID, userId, model.MessageBody);
        await emails.SendFeatureRequestAdminReplyAsync(
            request,
            Preview(model.MessageBody),
            BuildAbsoluteUrl("Details", "FeatureRequests", new { id = request.FeatureRequestID }),
            currentUser.Email ?? string.Empty);
        return RedirectToAction(nameof(FeatureRequestDetails), new { id = model.FeatureRequestID });
    }

    private string BuildAbsoluteUrl(string action, string controller, object routeValues)
    {
        return Url.Action(action, controller, routeValues, Request.Scheme)
            ?? $"{Request.Scheme}://{Request.Host}";
    }

    private static string Preview(string value)
    {
        var cleaned = value.ReplaceLineEndings(" ").Trim();
        return cleaned.Length <= 240 ? cleaned : $"{cleaned[..240]}...";
    }
}
