using Extrudex.API.DTOs.Materials;
using Extrudex.Domain.Entities;
using Extrudex.Infrastructure.Data;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace Extrudex.API.Controllers;

/// <summary>
/// Controller for managing material bases — the core polymer/material type
/// (e.g., PLA, PETG, ABS). Enforces consistent material naming across all spools.
/// </summary>
[ApiController]
[Route("api/material-bases")]
public class MaterialBasesController : ControllerBase
{
    private readonly ExtrudexDbContext _dbContext;
    private readonly ILogger<MaterialBasesController> _logger;

    public MaterialBasesController(
        ExtrudexDbContext dbContext,
        ILogger<MaterialBasesController> logger)
    {
        _dbContext = dbContext;
        _logger = logger;
    }

    /// <summary>
    /// Gets all material bases ordered by name.
    /// </summary>
    /// <returns>A list of all material bases with their densities.</returns>
    [HttpGet]
    [ProducesResponseType(typeof(IEnumerable<MaterialBaseResponse>), StatusCodes.Status200OK)]
    public async Task<ActionResult<IEnumerable<MaterialBaseResponse>>> GetMaterialBases()
    {
        _logger.LogDebug("Getting all material bases");

        var bases = await _dbContext.MaterialBases
            .OrderBy(mb => mb.Name)
            .Select(mb => MapToResponse(mb))
            .ToListAsync();

        return Ok(bases);
    }

    /// <summary>
    /// Gets a specific material base by ID.
    /// </summary>
    /// <param name="id">The unique identifier of the material base.</param>
    /// <returns>The material base details.</returns>
    [HttpGet("{id:guid}")]
    [ProducesResponseType(typeof(MaterialBaseResponse), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<ActionResult<MaterialBaseResponse>> GetMaterialBase(Guid id)
    {
        _logger.LogDebug("Getting material base {Id}", id);

        var mb = await _dbContext.MaterialBases.FindAsync(id);
        if (mb is null)
        {
            _logger.LogWarning("Material base {Id} not found", id);
            return NotFound(new { error = $"Material base with ID '{id}' not found." });
        }

        return Ok(MapToResponse(mb));
    }

    /// <summary>
    /// Creates a new material base.
    /// </summary>
    /// <param name="request">The material base creation request.</param>
    /// <returns>The created material base.</returns>
    [HttpPost]
    [ProducesResponseType(typeof(MaterialBaseResponse), StatusCodes.Status201Created)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    public async Task<ActionResult<MaterialBaseResponse>> CreateMaterialBase(
        [FromBody] CreateMaterialBaseRequest request)
    {
        _logger.LogInformation("Creating material base: {Name}", request.Name);

        var entity = new MaterialBase
        {
            Name = request.Name,
            DensityGperCm3 = request.DensityGperCm3
        };

        _dbContext.MaterialBases.Add(entity);
        await _dbContext.SaveChangesAsync();

        var response = MapToResponse(entity);
        return CreatedAtAction(nameof(GetMaterialBase), new { id = entity.Id }, response);
    }

    /// <summary>
    /// Updates an existing material base.
    /// </summary>
    /// <param name="id">The unique identifier of the material base to update.</param>
    /// <param name="request">The material base update request.</param>
    /// <returns>The updated material base.</returns>
    [HttpPut("{id:guid}")]
    [ProducesResponseType(typeof(MaterialBaseResponse), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    public async Task<ActionResult<MaterialBaseResponse>> UpdateMaterialBase(
        Guid id, [FromBody] UpdateMaterialBaseRequest request)
    {
        _logger.LogInformation("Updating material base {Id}", id);

        var entity = await _dbContext.MaterialBases.FindAsync(id);
        if (entity is null)
        {
            _logger.LogWarning("Material base {Id} not found for update", id);
            return NotFound(new { error = $"Material base with ID '{id}' not found." });
        }

        entity.Name = request.Name;
        entity.DensityGperCm3 = request.DensityGperCm3;

        await _dbContext.SaveChangesAsync();

        return Ok(MapToResponse(entity));
    }

    /// <summary>
    /// Deletes a material base.
    /// </summary>
    /// <param name="id">The unique identifier of the material base to delete.</param>
    [HttpDelete("{id:guid}")]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> DeleteMaterialBase(Guid id)
    {
        _logger.LogInformation("Deleting material base {Id}", id);

        var entity = await _dbContext.MaterialBases.FindAsync(id);
        if (entity is null)
        {
            _logger.LogWarning("Material base {Id} not found for deletion", id);
            return NotFound(new { error = $"Material base with ID '{id}' not found." });
        }

        _dbContext.MaterialBases.Remove(entity);
        await _dbContext.SaveChangesAsync();

        return NoContent();
    }

    // ── Mapping helper ──────────────────────────────────────────

    private static MaterialBaseResponse MapToResponse(MaterialBase mb) => new()
    {
        Id = mb.Id,
        Name = mb.Name,
        DensityGperCm3 = mb.DensityGperCm3,
        CreatedAt = mb.CreatedAt,
        UpdatedAt = mb.UpdatedAt
    };
}