using Health.Application.Medications;

namespace Health.WebApi.Endpoints;

public static class MedicationEndpoints
{
    public static void MapMedicationEndpoints(this WebApplication app)
    {
        var group = app.MapGroup("/api/medications").RequireAuthorization();

        group.MapGet("/", async (string? filter, HttpContext http, IMedicationService medications, CancellationToken ct) =>
        {
            var userId = GetUserId(http);
            var meds = await medications.ListAsync(userId, filter, ct);
            return Results.Ok(new { code = 0, data = meds, message = (string?)null });
        });

        group.MapPost("/", async (HttpRequest request, HttpContext http, IMedicationService medications, CancellationToken ct) =>
        {
            var userId = GetUserId(http);
            using var json = JsonDocument.Parse(await ReadBodyAsync(request, ct));
            var medId = await medications.CreateAsync(userId, ReadCreateRequest(json.RootElement), ct);
            return Results.Ok(new { code = 0, data = new { id = medId }, message = (string?)null });
        });

        group.MapPut("/{id:guid}", async (Guid id, HttpRequest request, HttpContext http, IMedicationService medications, CancellationToken ct) =>
        {
            var userId = GetUserId(http);
            using var json = JsonDocument.Parse(await ReadBodyAsync(request, ct));
            var updated = await medications.UpdateAsync(userId, id, ReadPatchRequest(json.RootElement), ct);
            if (!updated) return Results.Ok(new { code = 40004, message = "不存在" });

            return Results.Ok(new { code = 0, data = new { success = true }, message = (string?)null });
        });

        group.MapDelete("/{id:guid}", async (Guid id, HttpContext http, IMedicationService medications, CancellationToken ct) =>
        {
            var userId = GetUserId(http);
            await medications.DeleteAsync(userId, id, ct);
            return Results.Ok(new { code = 0, data = new { success = true }, message = (string?)null });
        });

        group.MapPost("/{id:guid}/confirm", async (Guid id, HttpContext http, IMedicationService medications, CancellationToken ct) =>
        {
            var userId = GetUserId(http);
            var taken = await medications.ToggleTodayTakenAsync(userId, id, ct);
            if (taken == null) return Results.Ok(new { code = 404, message = "药品不存在" });

            return Results.Ok(new { code = 0, data = new { taken }, message = (string?)null });
        });

        group.MapGet("/reminders", async (HttpContext http, IMedicationService medications, CancellationToken ct) =>
        {
            var userId = GetUserId(http);
            var reminders = await medications.GetRemindersAsync(userId, ct);
            return Results.Ok(new { code = 0, data = reminders, message = (string?)null });
        });

        group.MapPost("/{id:guid}/confirm-dose", async (Guid id, HttpContext http, IMedicationService medications, CancellationToken ct) =>
        {
            var userId = GetUserId(http);
            using var json = JsonDocument.Parse(await ReadBodyAsync(http.Request, ct));
            var timeStr = json.RootElement.GetProperty("scheduledTime").GetString()!;
            var statusStr = json.RootElement.TryGetProperty("status", out var st) ? st.GetString() : "taken";
            var scheduledTime = TimeOnly.Parse(timeStr);
            var status = statusStr == "taken" ? MedicationLogStatus.Taken : MedicationLogStatus.Skipped;

            var result = await medications.ConfirmDoseAsync(userId, id, scheduledTime, status, ct);
            if (result == null) return Results.Ok(new { code = 404, message = "药品不存在" });
            if (result == false) return Results.Ok(new { code = 0, data = new { success = false, message = "已打卡" }, message = (string?)null });

            return Results.Ok(new { code = 0, data = new { success = true }, message = (string?)null });
        });

        group.MapDelete("/{id:guid}/confirm-dose/{time}", async (Guid id, string time, HttpContext http, IMedicationService medications, CancellationToken ct) =>
        {
            var userId = GetUserId(http);
            await medications.CancelDoseAsync(userId, id, TimeOnly.Parse(time), ct);
            return Results.Ok(new { code = 0, data = new { success = true }, message = (string?)null });
        });
    }

    private static MedicationUpsertRequest ReadCreateRequest(JsonElement root)
    {
        var frequency = Enum.TryParse<MedicationFrequency>(
            root.TryGetProperty("frequency", out var f) ? f.GetString() : "Daily",
            out var freq)
            ? freq
            : MedicationFrequency.Daily;
        var source = Enum.TryParse<MedicationSource>(
            root.TryGetProperty("source", out var s) ? s.GetString() : "Manual",
            out var src)
            ? src
            : MedicationSource.Manual;

        return new MedicationUpsertRequest(
            root.GetProperty("name").GetString()!,
            root.TryGetProperty("dosage", out var dg) ? dg.GetString() : null,
            frequency,
            ReadTimes(root, "timeOfDay"),
            root.TryGetProperty("startDate", out var sd) && DateOnly.TryParse(sd.GetString(), out var startDate) ? startDate : null,
            root.TryGetProperty("endDate", out var ed) && DateOnly.TryParse(ed.GetString(), out var endDate) ? endDate : null,
            source,
            root.TryGetProperty("notes", out var nt) ? nt.GetString() : null);
    }

    private static MedicationPatchRequest ReadPatchRequest(JsonElement root)
    {
        MedicationFrequency? frequency = null;
        if (root.TryGetProperty("frequency", out var f) && Enum.TryParse<MedicationFrequency>(f.GetString(), out var freq))
            frequency = freq;

        return new MedicationPatchRequest(
            root.TryGetProperty("name", out var n) ? n.GetString() : null,
            root.TryGetProperty("dosage", out var dg) ? dg.GetString() : null,
            frequency,
            root.TryGetProperty("timeOfDay", out _) ? ReadTimes(root, "timeOfDay") : null,
            root.TryGetProperty("startDate", out var sd) && DateOnly.TryParse(sd.GetString(), out var startDate) ? startDate : null,
            root.TryGetProperty("endDate", out var ed) && DateOnly.TryParse(ed.GetString(), out var endDate) ? endDate : null,
            root.TryGetProperty("notes", out var nt) ? nt.GetString() : null);
    }

    private static List<TimeOnly> ReadTimes(JsonElement root, string propertyName)
    {
        if (!root.TryGetProperty(propertyName, out var tod) || tod.ValueKind != JsonValueKind.Array)
            return [];

        return tod.EnumerateArray()
            .Select(t => TimeOnly.TryParse(t.GetString(), out var parsed) ? parsed : (TimeOnly?)null)
            .Where(t => t.HasValue)
            .Select(t => t!.Value)
            .ToList();
    }

    private static async Task<string> ReadBodyAsync(HttpRequest request, CancellationToken ct)
    {
        request.EnableBuffering();
        using var reader = new StreamReader(request.Body, leaveOpen: true);
        var body = await reader.ReadToEndAsync(ct);
        request.Body.Position = 0;
        return string.IsNullOrWhiteSpace(body) ? "{}" : body;
    }

    private static Guid GetUserId(HttpContext http) =>
        Guid.TryParse(http.User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value, out var id) ? id : Guid.Empty;
}
