#nullable enable
using System.Text.Json;
using Baya.Application.Models.Patients;

namespace Baya.Application.Features.PatientCareRecords;

/// <summary>(De)serializes the family care plan's three JSON lists and assigns stable ids on write: a
/// caller-supplied id (&gt; 0) is preserved, a new item (id 0) gets the next free id — so ids stay stable
/// across edits within a plan.</summary>
internal static class CarePlanSerialization
{
    public static IReadOnlyList<MedicationDto> ParseMedications(string? json)
        => Parse<MedicationDto>(json);

    public static IReadOnlyList<RoutineItemDto> ParseRoutine(string? json)
        => Parse<RoutineItemDto>(json);

    public static IReadOnlyList<CareTaskDto> ParseTasks(string? json)
        => Parse<CareTaskDto>(json);

    public static (string Json, IReadOnlyList<MedicationDto> Items) AssignMedications(IReadOnlyList<MedicationDto>? items)
    {
        var assigned = AssignIds(items, (m, id) => m with { Id = id }, m => m.Id);
        return (JsonSerializer.Serialize(assigned), assigned);
    }

    public static (string Json, IReadOnlyList<RoutineItemDto> Items) AssignRoutine(IReadOnlyList<RoutineItemDto>? items)
    {
        var assigned = AssignIds(items, (r, id) => r with { Id = id }, r => r.Id);
        return (JsonSerializer.Serialize(assigned), assigned);
    }

    public static (string Json, IReadOnlyList<CareTaskDto> Items) AssignTasks(IReadOnlyList<CareTaskDto>? items)
    {
        var assigned = AssignIds(items, (t, id) => t with { Id = id }, t => t.Id);
        return (JsonSerializer.Serialize(assigned), assigned);
    }

    private static IReadOnlyList<T> Parse<T>(string? json)
    {
        if (string.IsNullOrWhiteSpace(json))
            return [];
        try
        {
            return JsonSerializer.Deserialize<List<T>>(json) ?? [];
        }
        catch (JsonException)
        {
            return [];
        }
    }

    private static IReadOnlyList<T> AssignIds<T>(IReadOnlyList<T>? items, Func<T, long, T> withId, Func<T, long> getId)
    {
        if (items is null || items.Count == 0)
            return [];

        var next = items.Select(getId).DefaultIfEmpty(0).Max() + 1;
        return items.Select(i => getId(i) > 0 ? i : withId(i, next++)).ToList();
    }
}
