using Extrudex.API.DTOs.PrintJobs;
using Extrudex.Domain.Interfaces;
using Microsoft.AspNetCore.Mvc;

namespace Extrudex.API.Controllers;

/// <summary>
/// Controller for cost analysis endpoints. Provides spool-level
/// cost breakdowns and aggregated COGS reporting.
/// </summary>
[ApiController]
[Route("api/cost-analysis")]
public class CostAnalysisController : ControllerBase
{
    private readonly ICostPerPrintService _costService;
    private readonly ILogger<CostAnalysisController> _logger;

    /// <summary>
    /// Initializes a new instance of the <see cref="CostAnalysisController"/> class.
    /// </summary>
    /// <param name="costService">The cost-per-print calculation service.</param>
    /// <param name="logger">The logger for diagnostic output.</param>
    public CostAnalysisController(
        ICostPerPrintService costService,
        ILogger<CostAnalysisController> logger)
    {
        _costService = costService;
        _logger = logger;
    }

    // ── POST /api/cost-analysis/spool ────────────────────────────

    /// <summary>
    /// Calculates cost breakdowns for all print jobs associated with a specific spool.
    /// Returns per-job costs plus an aggregated total. Jobs with missing cost data
    /// include warnings and null cost fields — the endpoint never throws for missing data.
    /// </summary>
    /// <param name="request">The request containing the spool identifier.</param>
    /// <returns>A spool-level cost summary with per-job breakdowns.</returns>
    /// <response code="200">Returns the spool cost breakdown with per-job details.</response>
    /// <response code="404">If the spool has no print jobs.</response>
    [HttpPost("spool")]
    [ProducesResponseType(typeof(SpoolCostResponse), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<ActionResult<SpoolCostResponse>> CalculateSpoolCost([FromBody] SpoolCostRequest request)
    {
        _logger.LogDebug("Calculating cost breakdown for spool {SpoolId}", request.SpoolId);

        var results = await _costService.CalculateBySpoolAsync(request.SpoolId);

        if (results.Count == 0)
        {
            return NotFound(new { error = $"No print jobs found for spool with ID '{request.SpoolId}'." });
        }

        // Build the spool-level summary
        var firstResult = results[0];
        var jobResponses = results.Select(MapCostToResponse).ToList();

        // Aggregate total cost and grams — only include jobs that have a valid cost
        var calculableJobs = results.Where(r => r.CostPerPrint.HasValue).ToList();
        var totalCost = calculableJobs.Count == results.Count
            ? Math.Round(calculableJobs.Sum(r => r.CostPerPrint!.Value), 4)
            : (decimal?)null;

        var aggregateWarnings = new List<string>();
        if (calculableJobs.Count < results.Count)
        {
            aggregateWarnings.Add(
                $"{results.Count - calculableJobs.Count} of {results.Count} print jobs have missing cost data. " +
                "Total cost reflects only jobs with complete data.");
        }

        var response = new SpoolCostResponse
        {
            SpoolId = request.SpoolId,
            SpoolSerial = firstResult.SpoolSerial,
            PurchasePrice = firstResult.PurchasePrice,
            WeightTotalGrams = firstResult.WeightTotalGrams,
            CostPerGram = firstResult.CostPerGram,
            TotalGramsConsumed = results.Sum(r => r.GramsDerived),
            TotalCost = totalCost,
            JobCount = results.Count,
            Jobs = jobResponses,
            Warnings = aggregateWarnings
        };

        return Ok(response);
    }

    /// <summary>
    /// Maps a domain CostPerPrintResult to an API CostPerPrintResponse DTO.
    /// </summary>
    private static CostPerPrintResponse MapCostToResponse(CostPerPrintResult r) => new()
    {
        PrintJobId = r.PrintJobId,
        PrintName = r.PrintName,
        SpoolId = r.SpoolId,
        SpoolSerial = r.SpoolSerial,
        MmExtruded = r.MmExtruded,
        GramsDerived = r.GramsDerived,
        PurchasePrice = r.PurchasePrice,
        WeightTotalGrams = r.WeightTotalGrams,
        CostPerGram = r.CostPerGram,
        CostPerPrint = r.CostPerPrint,
        Warnings = r.Warnings
    };
}