using MealPlan.Api.Extensions;
using MealPlan.Api.DTOs.Diet;
using MealPlan.Api.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace MealPlan.Api.Controllers;

[ApiController]
[Route("api/diet")]
[Authorize]
public class DietController : ControllerBase
{
    private readonly IDietTrackingService _dietService;

    public DietController(IDietTrackingService dietService) => _dietService = dietService;

    /// <summary>Daily intake summary with consumed totals, goals, and remaining amounts.</summary>
    [HttpGet("summary")]
    [ProducesResponseType(typeof(DailySummaryDto), StatusCodes.Status200OK)]
    public async Task<IActionResult> GetSummary([FromQuery] DateOnly? date, CancellationToken ct)
    {
        if (User.RequireUserId(out var userId) is { } unauthorized) return unauthorized;
        var targetDate = date ?? DateOnly.FromDateTime(DateTime.UtcNow);
        var summary = await _dietService.GetDailySummaryAsync(userId, targetDate, ct);
        return Ok(summary);
    }

    [HttpGet("goals")]
    [ProducesResponseType(typeof(UserDailyGoalsDto), StatusCodes.Status200OK)]
    public async Task<IActionResult> GetGoals(CancellationToken ct)
    {
        if (User.RequireUserId(out var userId) is { } unauthorized) return unauthorized;
        var goals = await _dietService.GetGoalsAsync(userId, ct);
        return Ok(goals ?? new UserDailyGoalsDto());
    }

    [HttpPut("goals")]
    [ProducesResponseType(typeof(UserDailyGoalsDto), StatusCodes.Status200OK)]
    public async Task<IActionResult> UpsertGoals([FromBody] UserDailyGoalsDto dto, CancellationToken ct)
    {
        if (User.RequireUserId(out var userId) is { } unauthorized) return unauthorized;
        var goals = await _dietService.UpsertGoalsAsync(userId, dto, ct);
        return Ok(goals);
    }

    [HttpGet("meals")]
    [ProducesResponseType(typeof(IReadOnlyList<MealConsumptionDto>), StatusCodes.Status200OK)]
    public async Task<IActionResult> GetMeals([FromQuery] DateOnly? date, CancellationToken ct)
    {
        if (User.RequireUserId(out var userId) is { } unauthorized) return unauthorized;
        var targetDate = date ?? DateOnly.FromDateTime(DateTime.UtcNow);
        var meals = await _dietService.GetMealsAsync(userId, targetDate, ct);
        return Ok(meals);
    }

    [HttpPost("meals")]
    [ProducesResponseType(typeof(MealConsumptionDto), StatusCodes.Status201Created)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> LogMeal([FromBody] LogMealDto dto, CancellationToken ct)
    {
        if (User.RequireUserId(out var userId) is { } unauthorized) return unauthorized;
        var meal = await _dietService.LogMealAsync(userId, dto, ct);
        if (meal is null)
        {
            return dto.RecipeId.HasValue
                ? NotFound(new { error = $"Recipe {dto.RecipeId} was not found." })
                : BadRequest(new { error = "Invalid meal log payload." });
        }

        return CreatedAtAction(nameof(GetMeals), new { date = meal.LogDate }, meal);
    }

    [HttpPost("meals/preview")]
    [ProducesResponseType(typeof(MealPreviewDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    public async Task<IActionResult> PreviewMeal([FromQuery] DateOnly? date, [FromBody] LogMealDto dto, CancellationToken ct)
    {
        if (User.RequireUserId(out var userId) is { } unauthorized) return unauthorized;
        var targetDate = date ?? DateOnly.FromDateTime(DateTime.UtcNow);
        var preview = await _dietService.PreviewMealAsync(userId, targetDate, dto, ct);
        return preview is null
            ? BadRequest(new { error = "Could not preview meal macros." })
            : Ok(preview);
    }

    [HttpGet("suggestions")]
    [ProducesResponseType(typeof(IReadOnlyList<DietSuggestionDto>), StatusCodes.Status200OK)]
    public async Task<IActionResult> GetSuggestions([FromQuery] DateOnly? date, [FromQuery] int limit = 5, CancellationToken ct = default)
    {
        if (User.RequireUserId(out var userId) is { } unauthorized) return unauthorized;
        var targetDate = date ?? DateOnly.FromDateTime(DateTime.UtcNow);
        var suggestions = await _dietService.GetMealSuggestionsAsync(userId, targetDate, limit, ct);
        return Ok(suggestions);
    }

    [HttpDelete("meals/{id:int}")]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> DeleteMeal(int id, CancellationToken ct)
    {
        if (User.RequireUserId(out var userId) is { } unauthorized) return unauthorized;
        var deleted = await _dietService.DeleteMealAsync(userId, id, ct);
        return deleted ? NoContent() : NotFound(new { error = $"Meal log {id} was not found." });
    }

    [HttpGet("drinks/catalog")]
    [ProducesResponseType(typeof(IReadOnlyList<DrinkCatalogItemDto>), StatusCodes.Status200OK)]
    public async Task<IActionResult> GetDrinkCatalog(CancellationToken ct)
    {
        var catalog = await _dietService.GetDrinkCatalogAsync(ct);
        return Ok(catalog);
    }

    [HttpGet("drinks")]
    [ProducesResponseType(typeof(IReadOnlyList<DrinkConsumptionDto>), StatusCodes.Status200OK)]
    public async Task<IActionResult> GetDrinks([FromQuery] DateOnly? date, CancellationToken ct)
    {
        if (User.RequireUserId(out var userId) is { } unauthorized) return unauthorized;
        var targetDate = date ?? DateOnly.FromDateTime(DateTime.UtcNow);
        var drinks = await _dietService.GetDrinksAsync(userId, targetDate, ct);
        return Ok(drinks);
    }

    [HttpPost("drinks")]
    [ProducesResponseType(typeof(DrinkConsumptionDto), StatusCodes.Status201Created)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> LogDrink([FromBody] LogDrinkDto dto, CancellationToken ct)
    {
        if (User.RequireUserId(out var userId) is { } unauthorized) return unauthorized;
        var drink = await _dietService.LogDrinkAsync(userId, dto, ct);
        if (drink is null)
        {
            return NotFound(new { error = "Drink catalog item was not found." });
        }

        return CreatedAtAction(nameof(GetDrinks), new { date = drink.LogDate }, drink);
    }

    [HttpDelete("drinks/{id:int}")]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> DeleteDrink(int id, CancellationToken ct)
    {
        if (User.RequireUserId(out var userId) is { } unauthorized) return unauthorized;
        var deleted = await _dietService.DeleteDrinkAsync(userId, id, ct);
        return deleted ? NoContent() : NotFound(new { error = $"Drink log {id} was not found." });
    }

    [HttpGet("reminders")]
    [ProducesResponseType(typeof(IReadOnlyList<DietReminderDto>), StatusCodes.Status200OK)]
    public async Task<IActionResult> GetReminders(CancellationToken ct)
    {
        if (User.RequireUserId(out var userId) is { } unauthorized) return unauthorized;
        var reminders = await _dietService.GetRemindersAsync(userId, ct);
        return Ok(reminders);
    }

    [HttpGet("reminders/due")]
    [ProducesResponseType(typeof(IReadOnlyList<DueReminderDto>), StatusCodes.Status200OK)]
    public async Task<IActionResult> GetDueReminders(
        [FromQuery] DateOnly? date,
        [FromQuery] TimeOnly? time,
        CancellationToken ct)
    {
        if (User.RequireUserId(out var userId) is { } unauthorized) return unauthorized;
        var due = await _dietService.GetDueRemindersAsync(userId, date, time, ct);
        return Ok(due);
    }

    [HttpPost("reminders")]
    [ProducesResponseType(typeof(DietReminderDto), StatusCodes.Status201Created)]
    public async Task<IActionResult> CreateReminder([FromBody] CreateDietReminderDto dto, CancellationToken ct)
    {
        if (User.RequireUserId(out var userId) is { } unauthorized) return unauthorized;
        var reminder = await _dietService.CreateReminderAsync(userId, dto, ct);
        return CreatedAtAction(nameof(GetReminders), reminder);
    }

    [HttpPut("reminders/{id:int}")]
    [ProducesResponseType(typeof(DietReminderDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> UpdateReminder(int id, [FromBody] UpdateDietReminderDto dto, CancellationToken ct)
    {
        if (User.RequireUserId(out var userId) is { } unauthorized) return unauthorized;
        var reminder = await _dietService.UpdateReminderAsync(userId, id, dto, ct);
        return reminder is null
            ? NotFound(new { error = $"Reminder {id} was not found." })
            : Ok(reminder);
    }

    [HttpDelete("reminders/{id:int}")]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> DeleteReminder(int id, CancellationToken ct)
    {
        if (User.RequireUserId(out var userId) is { } unauthorized) return unauthorized;
        var deleted = await _dietService.DeleteReminderAsync(userId, id, ct);
        return deleted ? NoContent() : NotFound(new { error = $"Reminder {id} was not found." });
    }

    [HttpGet("history")]
    [ProducesResponseType(typeof(IReadOnlyList<HistoryDayDto>), StatusCodes.Status200OK)]
    public async Task<IActionResult> GetHistory([FromQuery] DateOnly from, [FromQuery] DateOnly to, CancellationToken ct)
    {
        if (User.RequireUserId(out var userId) is { } unauthorized) return unauthorized;
        var history = await _dietService.GetHistoryAsync(userId, from, to, ct);
        return Ok(history);
    }

    [HttpGet("meals/recent")]
    [ProducesResponseType(typeof(IReadOnlyList<RecentMealDto>), StatusCodes.Status200OK)]
    public async Task<IActionResult> GetRecentMeals([FromQuery] int limit = 10, CancellationToken ct = default)
    {
        if (User.RequireUserId(out var userId) is { } unauthorized) return unauthorized;
        var meals = await _dietService.GetRecentMealsAsync(userId, limit, ct);
        return Ok(meals);
    }

    [HttpPost("meals/copy-yesterday")]
    [ProducesResponseType(typeof(CopyMealsResultDto), StatusCodes.Status200OK)]
    public async Task<IActionResult> CopyYesterdayMeals([FromQuery] DateOnly? date, CancellationToken ct)
    {
        if (User.RequireUserId(out var userId) is { } unauthorized) return unauthorized;
        var targetDate = date ?? DateOnly.FromDateTime(DateTime.UtcNow);
        var result = await _dietService.CopyYesterdayMealsAsync(userId, targetDate, ct);
        return Ok(result);
    }

    [HttpGet("favorites/recipes")]
    [ProducesResponseType(typeof(IReadOnlyList<FavoriteRecipeDto>), StatusCodes.Status200OK)]
    public async Task<IActionResult> GetFavoriteRecipes(CancellationToken ct)
    {
        if (User.RequireUserId(out var userId) is { } unauthorized) return unauthorized;
        return Ok(await _dietService.GetFavoriteRecipesAsync(userId, ct));
    }

    [HttpPost("favorites/recipes/{recipeId:int}")]
    [ProducesResponseType(typeof(FavoriteRecipeDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> AddFavoriteRecipe(int recipeId, CancellationToken ct)
    {
        if (User.RequireUserId(out var userId) is { } unauthorized) return unauthorized;
        var fav = await _dietService.AddFavoriteRecipeAsync(userId, recipeId, ct);
        return fav is null ? NotFound() : Ok(fav);
    }

    [HttpDelete("favorites/recipes/{recipeId:int}")]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    public async Task<IActionResult> RemoveFavoriteRecipe(int recipeId, CancellationToken ct)
    {
        if (User.RequireUserId(out var userId) is { } unauthorized) return unauthorized;
        var deleted = await _dietService.RemoveFavoriteRecipeAsync(userId, recipeId, ct);
        return deleted ? NoContent() : NotFound();
    }

    [HttpGet("favorites/catalog")]
    [ProducesResponseType(typeof(IReadOnlyList<FavoriteCatalogItemDto>), StatusCodes.Status200OK)]
    public async Task<IActionResult> GetFavoriteCatalogItems(CancellationToken ct)
    {
        if (User.RequireUserId(out var userId) is { } unauthorized) return unauthorized;
        return Ok(await _dietService.GetFavoriteCatalogItemsAsync(userId, ct));
    }

    [HttpPost("favorites/catalog/{catalogItemId:int}")]
    [ProducesResponseType(typeof(FavoriteCatalogItemDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> AddFavoriteCatalogItem(int catalogItemId, CancellationToken ct)
    {
        if (User.RequireUserId(out var userId) is { } unauthorized) return unauthorized;
        var fav = await _dietService.AddFavoriteCatalogItemAsync(userId, catalogItemId, ct);
        return fav is null ? NotFound() : Ok(fav);
    }

    [HttpDelete("favorites/catalog/{catalogItemId:int}")]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    public async Task<IActionResult> RemoveFavoriteCatalogItem(int catalogItemId, CancellationToken ct)
    {
        if (User.RequireUserId(out var userId) is { } unauthorized) return unauthorized;
        var deleted = await _dietService.RemoveFavoriteCatalogItemAsync(userId, catalogItemId, ct);
        return deleted ? NoContent() : NotFound();
    }
}
