using Health.Domain;
using Health.Domain.Entities;
using Health.Domain.Enums;

namespace Health.Application.Diets;

public sealed class DietService(IDietRepository diets) : IDietService
{
    private const int MaxCalories = 20000;   // 单条记录/单项热量上限（kcal）
    private readonly IDietRepository _diets = diets;

    public async Task<IReadOnlyList<DietRecordDto>> ListAsync(Guid userId, string? date, string? mealType, CancellationToken ct)
    {
        var recordedAt = DateOnly.TryParse(date, out var d) ? d : (DateOnly?)null;
        var parsedMealType = Enum.TryParse<MealType>(mealType, ignoreCase: true, out var mt) ? mt : (MealType?)null;

        var records = await _diets.ListAsync(userId, recordedAt, parsedMealType, ct);
        return records.Select(ToDto).ToList();
    }

    public async Task<Guid> CreateAsync(Guid userId, DietRecordCreateRequest request, CancellationToken ct)
    {
        ValidateCalories(request.TotalCalories, "总热量");
        ValidateScore(request.HealthScore);
        foreach (var item in request.FoodItems)
        {
            if (string.IsNullOrWhiteSpace(item.Name))
                throw new ValidationException("食物名称不能为空");
            ValidateCalories(item.Calories, $"食物「{item.Name.Trim()}」热量");
        }

        var record = new DietRecord
        {
            Id = Guid.NewGuid(),
            UserId = userId,
            MealType = request.MealType,
            TotalCalories = request.TotalCalories,
            HealthScore = request.HealthScore,
            RecordedAt = request.RecordedAt,
            CreatedAt = DateTime.UtcNow,
        };

        foreach (var item in request.FoodItems)
        {
            record.FoodItems.Add(new DietFoodItem
            {
                Id = Guid.NewGuid(),
                Name = item.Name.Trim(),
                Portion = item.Portion,
                Calories = item.Calories,
                SortOrder = item.SortOrder,
            });
        }

        await _diets.AddAsync(record, ct);
        await _diets.SaveChangesAsync(ct);
        return record.Id;
    }

    public async Task<bool> DeleteAsync(Guid userId, Guid recordId, CancellationToken ct)
    {
        var record = await _diets.GetOwnedAsync(userId, recordId, ct);
        if (record == null) return false;

        _diets.Delete(record);
        await _diets.SaveChangesAsync(ct);
        return true;
    }

    public async Task<bool> UpdateAsync(Guid userId, Guid recordId, DietRecordPatchRequest request, CancellationToken ct)
    {
        var record = await _diets.GetOwnedAsync(userId, recordId, ct);
        if (record == null) return false;

        ValidateCalories(request.TotalCalories, "总热量");
        ValidateScore(request.HealthScore);

        if (request.TotalCalories.HasValue) record.TotalCalories = request.TotalCalories.Value;
        if (request.HealthScore.HasValue) record.HealthScore = request.HealthScore.Value;
        await _diets.SaveChangesAsync(ct);
        return true;
    }

    private static void ValidateCalories(int? calories, string field)
    {
        if (calories.HasValue && (calories.Value < 0 || calories.Value > MaxCalories))
            throw new ValidationException($"{field}应在 0~{MaxCalories} kcal 之间，当前值 {calories.Value} 不合理");
    }

    private static void ValidateScore(int? score)
    {
        if (score.HasValue && (score.Value < 0 || score.Value > 100))
            throw new ValidationException($"健康评分应在 0~100 之间，当前值 {score.Value} 不合理");
    }

    private static DietRecordDto ToDto(DietRecord record) => new(
        record.Id,
        record.MealType.ToString(),
        record.TotalCalories,
        record.HealthScore,
        record.RecordedAt,
        record.FoodItems.OrderBy(f => f.SortOrder).Select(f => new DietFoodItemDto(f.Name, f.Portion, f.Calories)).ToList());
}
