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

namespace PlotLine.Controllers;

[Authorize]
public sealed class BooksController(IBookService books, IOnboardingService onboarding) : Controller
{
    public async Task<IActionResult> Details(int id)
    {
        var model = await books.GetDetailAsync(id);
        return model is null ? NotFound() : View(model);
    }

    public async Task<IActionResult> Create(int projectId)
    {
        var model = await books.GetCreateAsync(projectId);
        return model is null ? NotFound() : View("Edit", model);
    }

    public async Task<IActionResult> Edit(int id)
    {
        var model = await books.GetEditAsync(id);
        return model is null ? NotFound() : View(model);
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Save(BookEditViewModel model)
    {
        if (model.BeginMode is not (BookBeginModes.Manual or BookBeginModes.Import))
        {
            model.BeginMode = BookBeginModes.Manual;
        }

        if (!BookStatuses.IsValid(model.Status))
        {
            ModelState.AddModelError(nameof(model.Status), "Select a valid book status.");
        }

        if (!ModelState.IsValid)
        {
            await books.PopulateEditContextAsync(model);
            return View("Edit", model);
        }

        BookSaveResult result;
        try
        {
            result = await books.SaveAsync(model);
        }
        catch (SubscriptionLimitException ex) when (model.BookID == 0)
        {
            ModelState.AddModelError(string.Empty, ex.Message);
            var createModel = await books.GetCreateAsync(model.ProjectID);
            if (createModel is null)
            {
                return NotFound();
            }

            createModel.BookTitle = model.BookTitle;
            createModel.Subtitle = model.Subtitle;
            createModel.Tagline = model.Tagline;
            createModel.ShortDescription = model.ShortDescription;
            createModel.Status = model.Status;
            createModel.BookNumber = model.BookNumber;
            createModel.Description = model.Description;
            createModel.TargetWordCount = model.TargetWordCount;
            createModel.WritingStartDate = model.WritingStartDate;
            createModel.TargetCompletionDate = model.TargetCompletionDate;
            createModel.PublicationDate = model.PublicationDate;
            createModel.UsesExplicitScenes = model.UsesExplicitScenes;
            createModel.BeginMode = model.BeginMode;
            await books.PopulateEditContextAsync(createModel);
            return View("Edit", createModel);
        }

        if (!result.Succeeded)
        {
            model.BookID = result.BookID;
            ModelState.AddModelError(nameof(model.CoverUpload), result.ErrorMessage ?? "Cover image could not be uploaded.");
            await books.PopulateEditContextAsync(model);
            return View("Edit", model);
        }

        if (model.BookID == 0 && string.Equals(model.BeginMode, BookBeginModes.Import, StringComparison.Ordinal))
        {
            await onboarding.BeginManuscriptImportForBookAsync(model.ProjectID, result.BookID);
            return RedirectToAction("Index", "Onboarding");
        }

        return RedirectToAction(nameof(Details), new { id = result.BookID });
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Archive(int id, int projectId)
    {
        await books.ArchiveAsync(id);
        TempData["ArchiveMessage"] = "Book archived. You can restore it from Archived Items.";
        return RedirectToAction("Details", "Projects", new { id = projectId });
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> UnlinkManuscript(int id)
    {
        var result = await books.UnlinkManuscriptAsync(id);
        TempData["ManuscriptMessage"] = result is null
            ? "Unable to unlink manuscript. It may already be unlinked, or you may not have access."
            : result.Message;
        return RedirectToAction(nameof(Details), new { id });
    }
}
