using MealPlan.Api.DTOs.Recipe;
using MealPlan.Api.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace MealPlan.Api.Controllers;

/// <summary>Endpoints for creating recipes manually and importing from Excel.</summary>
[ApiController]
[Route("api/recipes")]
[Authorize]
public class RecipeManagementController : ControllerBase
{
    private readonly IRecipeManagementService _managementService;
    private readonly IRecipeExcelService _excelService;
    private readonly IRecipeCalorieRecalculationService _calorieRecalculation;

    public RecipeManagementController(
        IRecipeManagementService managementService,
        IRecipeExcelService excelService,
        IRecipeCalorieRecalculationService calorieRecalculation)
    {
        _managementService = managementService;
        _excelService = excelService;
        _calorieRecalculation = calorieRecalculation;
    }

    /// <summary>Creates a new recipe with ingredients and steps.</summary>
    [HttpPost]
    [ProducesResponseType(typeof(RecipeDetailDto), StatusCodes.Status201Created)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    public async Task<IActionResult> Create([FromBody] CreateRecipeDto dto, CancellationToken ct)
    {
        var recipe = await _managementService.CreateRecipeAsync(dto, ct);
        return CreatedAtAction(
            nameof(RecipesController.GetRecipe),
            "Recipes",
            new { id = recipe.Id },
            recipe);
    }

    /// <summary>Updates an existing recipe (replaces ingredients and steps).</summary>
    [HttpPut("{id:int}")]
    [ProducesResponseType(typeof(RecipeDetailDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> Update(int id, [FromBody] CreateRecipeDto dto, CancellationToken ct)
    {
        var recipe = await _managementService.UpdateRecipeAsync(id, dto, ct);
        return recipe is null
            ? NotFound(new { error = $"Recipe {id} was not found." })
            : Ok(recipe);
    }

    /// <summary>Downloads an Excel template for bulk recipe import.</summary>
    [HttpGet("import/template")]
    [ProducesResponseType(typeof(FileContentResult), StatusCodes.Status200OK)]
    public IActionResult DownloadTemplate()
    {
        var bytes = _excelService.GenerateTemplate();
        return File(
            bytes,
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
            "DailyMeals-recipe-import-template.xlsx");
    }

    /// <summary>Imports recipes from an uploaded .xlsx file (Recipes + Ingredients + Steps sheets).</summary>
    [HttpPost("import/excel")]
    [RequestSizeLimit(10_000_000)]
    [ProducesResponseType(typeof(RecipeImportResultDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    public async Task<IActionResult> ImportExcel(IFormFile? file, CancellationToken ct)
    {
        if (file is null || file.Length == 0)
        {
            return BadRequest(new { error = "No file uploaded." });
        }

        if (!file.FileName.EndsWith(".xlsx", StringComparison.OrdinalIgnoreCase))
        {
            return BadRequest(new { error = "Only .xlsx files are supported." });
        }

        await using var stream = file.OpenReadStream();
        var result = await _excelService.ImportAsync(stream, ct);
        return Ok(result);
    }

    /// <summary>Recalculates recipe macros from linked catalog ingredients (all active recipes).</summary>
    [HttpPost("recalculate-macros")]
    [ProducesResponseType(typeof(RecipeMacroRecalcResultDto), StatusCodes.Status200OK)]
    public async Task<IActionResult> RecalculateMacros(CancellationToken ct)
    {
        var result = await _managementService.RecalculateAllMacrosFromCatalogAsync(ct);
        return Ok(result);
    }

    /// <summary>Scales or AI-adjusts ingredient amounts to match a calorie target.</summary>
    [HttpPost("{id:int}/recalculate-calories")]
    [ProducesResponseType(typeof(RecalculateRecipeCaloriesResultDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> RecalculateCalories(
        int id,
        [FromBody] RecalculateRecipeCaloriesRequestDto request,
        CancellationToken ct)
    {
        try
        {
            var result = await _calorieRecalculation.RecalculateAsync(id, request, ct);
            return result is null
                ? NotFound(new { error = $"Recipe {id} was not found." })
                : Ok(result);
        }
        catch (InvalidOperationException ex)
        {
            return BadRequest(new { error = ex.Message });
        }
    }
}
