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 finishes — the surface finish descriptor
/// for a material. This is REQUIRED on every spool record. Default: "Basic".
/// </summary>
[ApiController]
[Route("api/material-finishes")]
public class MaterialFinishesController : ControllerBase
{
    private readonly ExtrudexDbContext _dbContext;
    private readonly ILogger<MaterialFinishesController> _logger;

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

    /// <summary>
    /// Gets all material finishes, optionally filtered by material base.
    /// </summary>
    /// <param name="materialBaseId">Optional filter: return finishes for this material base only.</param>
    /// <returns>A list of material finishes ordered by base material name, then finish name.</returns>
    [HttpGet]
    [ProducesResponseType(typeof(IEnumerable<MaterialFinishResponse>), StatusCodes.Status200OK)]
    public async Task<ActionResult<IEnumerable<MaterialFinishResponse>>> GetMaterialFinishes(
        [FromQuery] Guid? materialBaseId)
    {
        _logger.LogDebug("Getting material finishes, filter: {MaterialBaseId}", materialBaseId);

        var query = _dbContext.MaterialFinishes
            .Include(mf => mf.MaterialBase)
            .AsQueryable();

        if (materialBaseId.HasValue)
            query = query.Where(mf => mf.MaterialBaseId == materialBaseId.Value);

        var finishes = await query
            .OrderBy(mf => mf.MaterialBase.Name)
            .ThenBy(mf => mf.Name)
            .Select(mf => MapToResponse(mf))
            .ToListAsync();

        return Ok(finishes);
    }

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

        var mf = await _dbContext.MaterialFinishes
            .Include(mf => mf.MaterialBase)
            .FirstOrDefaultAsync(mf => mf.Id == id);

        if (mf is null)
        {
            _logger.LogWarning("Material finish {Id} not found", id);
            return NotFound(new { error = $"Material finish with ID '{id}' not found." });
        }

        return Ok(MapToResponse(mf));
    }

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

        var materialBase = await _dbContext.MaterialBases.FindAsync(request.MaterialBaseId);
        if (materialBase is null)
        {
            return BadRequest(new { error = $"MaterialBase with ID '{request.MaterialBaseId}' not found." });
        }

        var entity = new MaterialFinish
        {
            Name = request.Name,
            MaterialBaseId = request.MaterialBaseId
        };

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

        // Reload with navigation for response mapping
        await _dbContext.Entry(entity).Reference(e => e.MaterialBase).LoadAsync();

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

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

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

        var materialBase = await _dbContext.MaterialBases.FindAsync(request.MaterialBaseId);
        if (materialBase is null)
        {
            return BadRequest(new { error = $"MaterialBase with ID '{request.MaterialBaseId}' not found." });
        }

        entity.Name = request.Name;
        entity.MaterialBaseId = request.MaterialBaseId;

        await _dbContext.SaveChangesAsync();

        // Reload with navigation for response mapping
        await _dbContext.Entry(entity).Reference(e => e.MaterialBase).LoadAsync();

        return Ok(MapToResponse(entity));
    }

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

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

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

        return NoContent();
    }

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

    private static MaterialFinishResponse MapToResponse(MaterialFinish mf) => new()
    {
        Id = mf.Id,
        Name = mf.Name,
        MaterialBaseId = mf.MaterialBaseId,
        MaterialBaseName = mf.MaterialBase?.Name ?? string.Empty,
        CreatedAt = mf.CreatedAt,
        UpdatedAt = mf.UpdatedAt
    };
}