using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using ROLAC.API.DTOs.Giving;
using ROLAC.API.DTOs.Members;
using ROLAC.API.Hubs;
using ROLAC.API.Services;

namespace ROLAC.API.Controllers;

/// <summary>
/// Anonymous endpoints powering the mobile Sunday offering-entry page. The page
/// has no login yet, so it cannot reach the auth-gated members/categories/
/// offering-sessions APIs — these expose just what it needs (active categories,
/// a name-only member typeahead, and append-one-line).
/// </summary>
[ApiController]
[Route("api/offering-entry")]
[AllowAnonymous]
public class OfferingEntryController : ControllerBase
{
    private readonly IOfferingSessionService       _sessions;
    private readonly IGivingCategoryService         _categories;
    private readonly IMemberService                 _members;
    private readonly IHubContext<OfferingEntryHub>  _hub;

    public OfferingEntryController(
        IOfferingSessionService sessions,
        IGivingCategoryService categories,
        IMemberService members,
        IHubContext<OfferingEntryHub> hub)
    {
        _sessions   = sessions;
        _categories = categories;
        _members    = members;
        _hub        = hub;
    }

    // Seed the page in one round-trip: active categories + today's session state.
    [HttpGet("bootstrap")]
    public async Task<IActionResult> Bootstrap([FromQuery] DateOnly date)
        => Ok(new OfferingEntryBootstrapDto
        {
            SessionDate = date.ToString("yyyy-MM-dd"),
            Categories  = await _categories.GetAllAsync(false),
            Summary     = await _sessions.GetEntrySummaryAsync(date),
        });

    // Name-only member suggestions for the giver typeahead.
    [HttpGet("members")]
    public async Task<IActionResult> SearchMembers([FromQuery] string? search, [FromQuery] int take = 10)
        => Ok(await _sessions.SearchMembersForEntryAsync(search, Math.Clamp(take, 1, 25)));

    // Quick-add a giver who isn't on file yet (created as a Visitor). Reuses the
    // member service directly — role checks live on MembersController, so this
    // anonymous path is the intended public entry point for the mobile page.
    [HttpPost("members")]
    public async Task<IActionResult> QuickAddMember([FromBody] QuickAddMemberRequest request)
    {
        var id = await _members.CreateAsync(new CreateMemberRequest
        {
            FirstName_en       = request.FirstName_en,
            LastName_en        = request.LastName_en,
            NickName           = request.NickName,
            FirstName_zh       = request.FirstName_zh,
            LastName_zh        = request.LastName_zh,
            Entity             = request.Entity,
            PhoneCell          = request.PhoneCell,
            Status             = "Visitor",
            Country            = "USA",
            LanguagePreference = "en",
        });
        return Ok(new MemberTypeaheadDto
        {
            Id = id, NickName = request.NickName,
            FirstName_en = request.FirstName_en, LastName_en = request.LastName_en,
            Entity = request.Entity,
        });
    }

    // Append one offering line to the date's session (find-or-create), then
    // broadcast it to everyone viewing that date.
    [HttpPost("lines")]
    public async Task<IActionResult> AppendLine([FromBody] AppendOfferingLineRequest request)
    {
        var result = await _sessions.AppendLineAsync(request.Date, request.Line);
        await _hub.Clients.Group(result.SessionDate).SendAsync("LineAdded", result);
        return Ok(result);
    }

    // ── Paper-proof PDF for the date's session (merged client-side) ──────────
    // Date-keyed so the anonymous page (which has no session id) can attach the
    // count sheet / envelope photos. Mirrors OfferingSessionsController's proof
    // validation; the desktop session page reviews/deletes the result.

    [HttpGet("proof")]
    public async Task<IActionResult> GetProof([FromQuery] DateOnly date)
    {
        var result = await _sessions.OpenProofForDateAsync(date);
        if (result is null) return NoContent();   // no session/proof yet — client merges nothing
        return File(result.Value.stream, result.Value.contentType);
    }

    [HttpPost("proof")]
    [RequestSizeLimit(52_428_800)]   // 50 MB — a merged multi-image PDF is larger than one receipt
    public async Task<IActionResult> UploadProof([FromForm] DateOnly date, IFormFile file)
    {
        if (file is null || file.Length == 0) return BadRequest(new { message = "No file." });
        if (file.ContentType != "application/pdf") return BadRequest(new { message = "Proof must be a PDF." });
        await using var stream = file.OpenReadStream();
        await _sessions.SaveProofForDateAsync(date, stream, file.FileName);
        return NoContent();
    }
}
