using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using PowderCoating.Application.Interfaces;
using PowderCoating.Core.Entities;
using PowderCoating.Core.Interfaces;
using PowderCoating.Shared.Constants;

namespace PowderCoating.Web.Controllers;

/// <summary>
/// Manages the platform changelog / release notes feed.
/// <para>
/// Has two access tiers: the public changelog (<see cref="Index"/>) is available
/// to any authenticated tenant user so they can review what has changed; the
/// management actions (Create, Edit, TogglePublish, Delete) are restricted to
/// SuperAdmins because only platform staff should author release content.
/// </para>
/// </summary>
[Authorize]
public class ReleaseNotesController : Controller
{
    private readonly IUnitOfWork _unitOfWork;
    private readonly IInAppNotificationService _inApp;

    public ReleaseNotesController(IUnitOfWork unitOfWork, IInAppNotificationService inApp)
    {
        _unitOfWork = unitOfWork;
        _inApp = inApp;
    }

    // ── Public: Changelog ────────────────────────────────────────────────────

    /// <summary>
    /// Renders the public changelog — shows only published release notes ordered
    /// newest-first.  Drafts are invisible to ordinary users so SuperAdmins can
    /// prepare notes in advance without surfacing them prematurely.
    /// </summary>
    public async Task<IActionResult> Index()
    {
        var notes = (await _unitOfWork.ReleaseNotes.FindAsync(r => r.IsPublished))
            .OrderByDescending(r => r.ReleasedAt)
            .ThenByDescending(r => r.Id)
            .ToList();
        return View(notes);
    }

    // ── SuperAdmin: Manage ───────────────────────────────────────────────────

    /// <summary>
    /// Returns the SuperAdmin management list of all release notes (published and draft alike), ordered newest-first.
    /// </summary>
    [Authorize(Policy = AppConstants.Policies.SuperAdminOnly)]
    public async Task<IActionResult> Manage()
    {
        var notes = (await _unitOfWork.ReleaseNotes.GetAllAsync())
            .OrderByDescending(r => r.ReleasedAt)
            .ThenByDescending(r => r.Id)
            .ToList();
        return View(notes);
    }

    /// <summary>
    /// Returns the Create form pre-populated with today's UTC date and the "Feature" tag as sensible defaults.
    /// </summary>
    [Authorize(Policy = AppConstants.Policies.SuperAdminOnly)]
    public IActionResult Create()
    {
        return View(new ReleaseNote { ReleasedAt = DateTime.UtcNow, Tag = "Feature" });
    }

    /// <summary>
    /// Persists a new release note. New notes start unpublished unless the form explicitly sets IsPublished = true.
    /// </summary>
    [HttpPost, ValidateAntiForgeryToken]
    [Authorize(Policy = AppConstants.Policies.SuperAdminOnly)]
    public async Task<IActionResult> Create(ReleaseNote model)
    {
        if (!ModelState.IsValid)
            return View(model);

        model.CreatedAt = DateTime.UtcNow;
        model.CreatedByUserId = User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;
        model.CreatedByUserName = User.Identity?.Name;

        await _unitOfWork.ReleaseNotes.AddAsync(model);
        await _unitOfWork.CompleteAsync();

        if (model.IsPublished)
            await NotifyAllTenantsAsync(model);

        TempData["Success"] = $"Release note v{model.Version} created.";
        return RedirectToAction(nameof(Manage));
    }

    /// <summary>Returns the Edit form loaded from the database by primary key.</summary>
    [Authorize(Policy = AppConstants.Policies.SuperAdminOnly)]
    public async Task<IActionResult> Edit(int id)
    {
        var note = await _unitOfWork.ReleaseNotes.GetByIdAsync(id);
        if (note == null) return NotFound();
        return View(note);
    }

    /// <summary>
    /// Applies the edited values to the tracked entity using explicit field mapping to prevent
    /// over-posting attacks and ensure audit fields are never overwritten.
    /// </summary>
    [HttpPost, ValidateAntiForgeryToken]
    [Authorize(Policy = AppConstants.Policies.SuperAdminOnly)]
    public async Task<IActionResult> Edit(int id, ReleaseNote model)
    {
        if (id != model.Id) return BadRequest();
        if (!ModelState.IsValid)
            return View(model);

        var note = await _unitOfWork.ReleaseNotes.GetByIdAsync(id);
        if (note == null) return NotFound();

        note.Version     = model.Version;
        note.Title       = model.Title;
        note.Body        = model.Body;
        note.Tag         = model.Tag;
        note.IsPublished = model.IsPublished;
        note.ReleasedAt  = model.ReleasedAt;
        note.UpdatedAt   = DateTime.UtcNow;

        await _unitOfWork.CompleteAsync();

        TempData["Success"] = $"Release note v{note.Version} updated.";
        return RedirectToAction(nameof(Manage));
    }

    /// <summary>
    /// Toggles the published state of a release note. Publishing fires an in-app notification to all tenants.
    /// </summary>
    [HttpPost, ValidateAntiForgeryToken]
    [Authorize(Policy = AppConstants.Policies.SuperAdminOnly)]
    public async Task<IActionResult> TogglePublish(int id)
    {
        var note = await _unitOfWork.ReleaseNotes.GetByIdAsync(id);
        if (note == null) return NotFound();

        var wasPublished = note.IsPublished;
        note.IsPublished = !note.IsPublished;
        note.UpdatedAt   = DateTime.UtcNow;
        await _unitOfWork.CompleteAsync();

        if (note.IsPublished && !wasPublished)
            await NotifyAllTenantsAsync(note);

        TempData["Success"] = note.IsPublished
            ? $"v{note.Version} published — now visible to all users."
            : $"v{note.Version} unpublished — hidden from users.";
        return RedirectToAction(nameof(Manage));
    }

    /// <summary>
    /// Permanently (hard) deletes a release note. Release notes are platform metadata and do not use soft delete.
    /// </summary>
    [HttpPost, ValidateAntiForgeryToken]
    [Authorize(Policy = AppConstants.Policies.SuperAdminOnly)]
    public async Task<IActionResult> Delete(int id)
    {
        var note = await _unitOfWork.ReleaseNotes.GetByIdAsync(id);
        if (note == null) return NotFound();

        await _unitOfWork.ReleaseNotes.DeleteAsync(note);
        await _unitOfWork.CompleteAsync();

        TempData["Success"] = $"Release note v{note.Version} deleted.";
        return RedirectToAction(nameof(Manage));
    }

    /// <summary>
    /// Fans out a "What's New" in-app notification to every active tenant company when a release note is published.
    /// </summary>
    private Task NotifyAllTenantsAsync(ReleaseNote note)
    {
        var title = $"What's New — {note.Title}";
        var message = note.Tag != null ? $"[{note.Tag}] See the latest updates in What's New." : "See the latest updates in What's New.";
        return _inApp.CreateForAllCompaniesAsync(title, message, "ReleaseNote", "/ReleaseNotes");
    }
}
