using Extrudex.API.DTOs.UsageLogs;
using Extrudex.Domain.Enums;
using Extrudex.Domain.Interfaces;
using Microsoft.AspNetCore.Mvc;

namespace Extrudex.API.Controllers;

/// <summary>
/// API controller for recording and querying filament usage logs.
/// Usage logs provide a fine-grained audit trail of filament consumption
/// from printer integrations or manual input.
/// </summary>
[ApiController]
[Route("api/[controller]")]
[Produces("application/json")]
public class UsageLogsController : ControllerBase
{
    private readonly IUsageLogService _usageLogService;

    /// <summary>
    /// Initializes a new instance of the <see cref="UsageLogsController"/> class.
    /// </summary>
    /// <param name="usageLogService">The usage log service for recording and querying usage.</param>
    public UsageLogsController(IUsageLogService usageLogService)
    {
        _usageLogService = usageLogService;
    }

    /// <summary>
    /// Records a new filament usage entry.
    /// </summary>
    /// <param name="request">The usage entry details.</param>
    /// <returns>The created usage log entry.</returns>
    [HttpPost]
    [ProducesResponseType(typeof(UsageLogResponse), StatusCodes.Status201Created)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    public async Task<ActionResult<UsageLogResponse>> Create([FromBody] CreateUsageLogRequest request)
    {
        if (!Enum.TryParse<DataSource>(request.DataSource, ignoreCase: true, out var dataSource))
        {
            return BadRequest($"Invalid data source: '{request.DataSource}'. Valid values: Mqtt, Moonraker, Manual.");
        }

        var entry = await _usageLogService.RecordUsageAsync(
            spoolId: request.SpoolId,
            gramsUsed: request.GramsUsed,
            dataSource: dataSource,
            printerId: request.PrinterId,
            printJobId: request.PrintJobId,
            mmExtruded: request.MmExtruded,
            usageTimestamp: request.UsageTimestamp,
            notes: request.Notes
        );

        return CreatedAtAction(
            nameof(GetBySpool),
            new { spoolId = entry.SpoolId },
            MapToResponse(entry));
    }

    /// <summary>
    /// Gets usage logs for a specific spool, ordered by most recent first.
    /// </summary>
    /// <param name="spoolId">The spool ID to filter by.</param>
    /// <returns>A collection of usage log entries for the spool.</returns>
    [HttpGet("spool/{spoolId:guid}")]
    [ProducesResponseType(typeof(IEnumerable<UsageLogResponse>), StatusCodes.Status200OK)]
    public async Task<ActionResult<IEnumerable<UsageLogResponse>>> GetBySpool(Guid spoolId)
    {
        var logs = await _usageLogService.GetBySpoolAsync(spoolId);
        return Ok(logs.Select(MapToResponse));
    }

    /// <summary>
    /// Gets usage logs for a specific printer, ordered by most recent first.
    /// </summary>
    /// <param name="printerId">The printer ID to filter by.</param>
    /// <returns>A collection of usage log entries for the printer.</returns>
    [HttpGet("printer/{printerId:guid}")]
    [ProducesResponseType(typeof(IEnumerable<UsageLogResponse>), StatusCodes.Status200OK)]
    public async Task<ActionResult<IEnumerable<UsageLogResponse>>> GetByPrinter(Guid printerId)
    {
        var logs = await _usageLogService.GetByPrinterAsync(printerId);
        return Ok(logs.Select(MapToResponse));
    }

    /// <summary>
    /// Gets usage logs for a specific print job, ordered by most recent first.
    /// </summary>
    /// <param name="printJobId">The print job ID to filter by.</param>
    /// <returns>A collection of usage log entries for the print job.</returns>
    [HttpGet("print-job/{printJobId:guid}")]
    [ProducesResponseType(typeof(IEnumerable<UsageLogResponse>), StatusCodes.Status200OK)]
    public async Task<ActionResult<IEnumerable<UsageLogResponse>>> GetByPrintJob(Guid printJobId)
    {
        var logs = await _usageLogService.GetByPrintJobAsync(printJobId);
        return Ok(logs.Select(MapToResponse));
    }

    /// <summary>
    /// Maps a UsageLog domain entity to a UsageLogResponse DTO.
    /// </summary>
    private static UsageLogResponse MapToResponse(Domain.Entities.UsageLog log) => new()
    {
        Id = log.Id,
        SpoolId = log.SpoolId,
        PrinterId = log.PrinterId,
        PrintJobId = log.PrintJobId,
        GramsUsed = log.GramsUsed,
        MmExtruded = log.MmExtruded,
        UsageTimestamp = log.UsageTimestamp,
        DataSource = log.DataSource.ToString(),
        Notes = log.Notes,
        CreatedAt = log.CreatedAt,
        UpdatedAt = log.UpdatedAt
    };
}