using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using ROLAC.API.Authorization;
using ROLAC.API.Data;
using ROLAC.API.DTOs.Notifications;
using ROLAC.API.Services.Logging;
using ROLAC.API.Services.Notifications;

namespace ROLAC.API.Controllers;

/// <summary>
/// Admin endpoints for the notification module (API-only phase). Binding-code generation, group
/// management, send history, and manual send — the manual send endpoints are the only way to fire
/// a message before a UI exists; programmatic callers use the services directly.
/// </summary>
[ApiController]
[Route("api/notifications")]
[Authorize]
public sealed class NotificationsController : ControllerBase
{
    private readonly IEmailService _email;
    private readonly ILineNotificationService _line;
    private readonly IWebPushService _webPush;
    private readonly AppDbContext _db;
    private readonly CurrentUserAccessor _currentUser;

    public NotificationsController(
        IEmailService email, ILineNotificationService line, IWebPushService webPush,
        AppDbContext db, CurrentUserAccessor currentUser)
    {
        _email = email;
        _line = line;
        _webPush = webPush;
        _db = db;
        _currentUser = currentUser;
    }

    [HttpPost("members/{id:int}/line-binding-code")]
    public async Task<IActionResult> GenerateBindingCode(int id, CancellationToken ct)
        => Ok(new { code = await _line.GenerateLineBindingCodeAsync(id, ct) });

    [HttpGet("groups")]
    public async Task<IActionResult> Groups(CancellationToken ct)
        => Ok(await _db.MessagingGroups
            .OrderBy(g => g.Id)
            .Select(g => new { g.Id, g.Name, g.IsActive, g.RegisteredAt })
            .ToListAsync(ct));

    [HttpPut("groups/{id:int}")]
    public async Task<IActionResult> UpdateGroup(int id, [FromBody] UpdateGroupRequest request, CancellationToken ct)
    {
        var group = await _db.MessagingGroups.FirstOrDefaultAsync(g => g.Id == id, ct);
        if (group is null) return NotFound();

        group.Name = request.Name;
        group.IsActive = request.IsActive;
        await _db.SaveChangesAsync(ct);
        return NoContent();
    }

    [HttpGet("history")]
    public async Task<IActionResult> History(
        [FromQuery] int page = 1, [FromQuery] int pageSize = 50, CancellationToken ct = default)
    {
        var size = Math.Clamp(pageSize, 1, 200);
        var skip = (Math.Max(page, 1) - 1) * size;

        var query = _db.NotificationLogs.OrderByDescending(l => l.SentAt);
        var total = await query.CountAsync(ct);
        var items = await query
            .Skip(skip).Take(size)
            .Select(l => new
            {
                l.Id, l.Channel, l.TargetType, l.TargetExternalId, l.Subject,
                l.Status, l.Error, l.SentByUserId, l.SentAt,
            })
            .ToListAsync(ct);

        return Ok(new { total, items });
    }

    [HttpPost("send-line")]
    public async Task<IActionResult> SendLine([FromBody] SendLineRequest request, CancellationToken ct)
        => Ok(await _line.SendLineAsync(
            request.Body, request.MemberIds ?? [], request.GroupIds ?? [],
            _currentUser.UserIdOrSystem, ct));

    [HttpPost("send-email")]
    public async Task<IActionResult> SendEmail([FromBody] SendEmailRequest request, CancellationToken ct)
        => Ok(await _email.SendAsync(new EmailMessage(
            MemberIds: request.MemberIds ?? [],
            Addresses: request.Addresses ?? [],
            Subject: request.Subject,
            HtmlBody: request.HtmlBody,
            Attachments: null,
            SentByUserId: _currentUser.UserIdOrSystem), ct));

    // Manual one-to-(few) Web Push send. Guarded by Settings:Write (sending is an admin capability);
    // the Member Management "send push" test action calls this for a single member.
    [HttpPost("send-webpush")]
    [HasPermission(Modules.Settings, PermissionActions.Write)]
    public async Task<IActionResult> SendWebPush([FromBody] SendWebPushRequest request, CancellationToken ct)
        => Ok(await _webPush.SendToMembersAsync(
            request.MemberIds ?? [],
            new WebPushPayload(request.Title, request.Body, request.Url),
            _currentUser.UserIdOrSystem, ct));
}
