using System.Text;
using System.Text.Json;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using ROLAC.API.DTOs.Notifications;
using ROLAC.API.Services.Notifications;

namespace ROLAC.API.Controllers;

/// <summary>
/// Anonymous Line webhook. Verifies the X-Line-Signature over the raw body, then dispatches
/// follow/message/join/leave events. Always returns 200 for valid payloads so Line does not retry;
/// returns 400 only on signature failure.
/// </summary>
[ApiController]
[Route("api/line")]
[AllowAnonymous]
public sealed class LineWebhookController : ControllerBase
{
    private static readonly JsonSerializerOptions JsonOpts = new() { PropertyNameCaseInsensitive = true };

    private readonly ILineNotificationService _line;
    private readonly IMessageChannel _channel;
    private readonly INotificationSettingsService _settings;

    public LineWebhookController(
        ILineNotificationService line, IMessageChannel channel, INotificationSettingsService settings)
    {
        _line = line;
        _channel = channel;
        _settings = settings;
    }

    [HttpPost("webhook")]
    [RequestSizeLimit(262_144)]
    public async Task<IActionResult> Webhook(CancellationToken ct)
    {
        using var reader = new StreamReader(Request.Body, Encoding.UTF8);
        var rawBody = await reader.ReadToEndAsync(ct);
        var signature = Request.Headers["X-Line-Signature"].FirstOrDefault();

        if (!LineSignature.IsValid(_settings.GetLine().ChannelSecret, Encoding.UTF8.GetBytes(rawBody), signature))
            return BadRequest();

        var payload = JsonSerializer.Deserialize<LineWebhookPayload>(rawBody, JsonOpts);
        if (payload?.Events is not null)
            foreach (var evt in payload.Events)
                await DispatchAsync(evt, ct);

        return Ok();
    }

    private async Task DispatchAsync(LineWebhookEvent evt, CancellationToken ct)
    {
        switch (evt.Type)
        {
            case "follow":
                if (evt.ReplyToken is not null)
                    await _channel.ReplyAsync(evt.ReplyToken, "歡迎！請輸入您的綁定碼以連結教會帳號。", ct);
                break;

            case "message":
                if (evt.Message?.Type == "text"
                    && evt.Source?.UserId is { } userId
                    && evt.Message.Text is { } text)
                {
                    var result = await _line.TryBindMemberAsync(userId, text, ct);
                    if (evt.ReplyToken is not null)
                        await _channel.ReplyAsync(evt.ReplyToken, result.Message, ct);
                }
                break;

            case "join":
                if (evt.Source?.GroupId is { } joinGroupId)
                {
                    await _line.RegisterGroupAsync(joinGroupId, ct);
                    if (evt.ReplyToken is not null)
                        await _channel.ReplyAsync(evt.ReplyToken, "已加入群組，請至後台命名此群組。", ct);
                }
                break;

            case "leave":
                if (evt.Source?.GroupId is { } leaveGroupId)
                    await _line.DeactivateGroupAsync(leaveGroupId, ct);
                break;
        }
    }
}
