using AdsServingService.API.Application.Queries;
using AdsServingService.API.Application.Events;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace AdsServingService.API.Controllers;

/// <summary>
/// EN: API Controller for serving ads in real-time.
/// VI: API Controller serve quảng cáo theo thời gian thực.
/// </summary>
[ApiController]
[Authorize]
[Route("api/v1/ads")]
[Produces("application/json")]
public class AdsController : ControllerBase
{
    private readonly IMediator _mediator;
    private readonly IAdServingEventPublisher _eventPublisher;
    private readonly ILogger<AdsController> _logger;

    public AdsController(
        IMediator mediator,
        IAdServingEventPublisher eventPublisher,
        ILogger<AdsController> logger)
    {
        _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
        _eventPublisher = eventPublisher ?? throw new ArgumentNullException(nameof(eventPublisher));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }

    /// <summary>
    /// EN: Serve an ad based on user context (less than 100ms target).
    /// VI: Serve quảng cáo dựa trên ngữ cảnh người dùng (mục tiêu dưới 100ms).
    /// </summary>
    [HttpPost("serve")]
    [ProducesResponseType(typeof(ServedAdDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    public async Task<ActionResult<ServedAdDto>> ServeAd([FromBody] ServeAdRequest request)
    {
        var query = new ServeAdQuery
        {
            UserId = request.UserId,
            PlacementType = request.PlacementType,
            UserContext = request.UserContext ?? new Dictionary<string, string>()
        };

        var result = await _mediator.Send(query);

        if (result == null)
            return NoContent();

        return Ok(new { success = true, data = result });
    }

    /// <summary>
    /// EN: Track ad impression (fire-and-forget).
    /// VI: Track impression quảng cáo (fire-and-forget).
    /// </summary>
    [HttpPost("events/impression")]
    [ProducesResponseType(StatusCodes.Status202Accepted)]
    public async Task<IActionResult> TrackImpression([FromBody] ImpressionEvent impressionEvent)
    {
        _logger.LogInformation("Impression tracked for Ad {AdId}", impressionEvent.AdId);

        await _eventPublisher.PublishImpressionTrackedAsync(
            new AdImpressionTrackedEvent
            {
                AdId = impressionEvent.AdId,
                UserId = impressionEvent.UserId,
                Timestamp = impressionEvent.Timestamp,
            },
            HttpContext.RequestAborted);

        return Accepted(new { success = true, data = new { message = "Impression tracked" } });
    }

    /// <summary>
    /// EN: Track ad click (fire-and-forget).
    /// VI: Track click quảng cáo (fire-and-forget).
    /// </summary>
    [HttpPost("events/click")]
    [ProducesResponseType(StatusCodes.Status202Accepted)]
    public async Task<IActionResult> TrackClick([FromBody] ClickEvent clickEvent)
    {
        _logger.LogInformation("Click tracked for Ad {AdId}", clickEvent.AdId);

        await _eventPublisher.PublishClickTrackedAsync(
            new AdClickTrackedEvent
            {
                AdId = clickEvent.AdId,
                UserId = clickEvent.UserId,
                Timestamp = clickEvent.Timestamp,
            },
            HttpContext.RequestAborted);

        return Accepted(new { success = true, data = new { message = "Click tracked" } });
    }
}

public record ServeAdRequest
{
    public Guid UserId { get; init; }
    public string PlacementType { get; init; } = "feed";
    public Dictionary<string, string>? UserContext { get; init; }
}

public record ImpressionEvent
{
    public Guid AdId { get; init; }
    public Guid UserId { get; init; }
    public DateTime Timestamp { get; init; }
}

public record ClickEvent
{
    public Guid AdId { get; init; }
    public Guid UserId { get; init; }
    public DateTime Timestamp { get; init; }
}
