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, AppDbContext db, CancellationToken ct) =>
        {
            var userId = GetUserId(http);
            var query = db.Medications.Where(m => m.UserId == userId);
            if (filter == "active") query = query.Where(m => m.IsActive);
            else if (filter == "inactive") query = query.Where(m => !m.IsActive);
            var meds = await query.OrderByDescending(m => m.CreatedAt).Select(m => new
            {
                m.Id, m.Name, m.Dosage, Frequency = m.Frequency.ToString(),
                m.TimeOfDay, m.StartDate, m.EndDate, m.IsActive, m.Notes,
                Source = m.Source.ToString(), m.CreatedAt, m.UpdatedAt,
                todayTaken = m.Logs.Any(l => l.CreatedAt.Date == DateTime.UtcNow.Date && l.Status == MedicationLogStatus.Taken)
            }).ToListAsync(ct);
            return Results.Ok(new { code = 0, data = meds, message = (string?)null });
        });

        group.MapPost("/", async (HttpRequest request, HttpContext http, AppDbContext db, CancellationToken ct) =>
        {
            var userId = GetUserId(http);
            request.EnableBuffering();
            using var reader = new StreamReader(request.Body);
            var body = await reader.ReadToEndAsync(ct);
            request.Body.Position = 0;
            using var json = JsonDocument.Parse(body);
            var root = json.RootElement;
            var med = new Medication
            {
                Id = Guid.NewGuid(), UserId = userId,
                Name = root.GetProperty("name").GetString()!,
                Dosage = root.TryGetProperty("dosage", out var dg) ? dg.GetString() : null,
                Frequency = Enum.TryParse<MedicationFrequency>(root.TryGetProperty("frequency", out var f) ? f.GetString() : "Daily", out var freq) ? freq : MedicationFrequency.Daily,
                Source = Enum.TryParse<MedicationSource>(root.TryGetProperty("source", out var s) ? s.GetString() : "Manual", out var src) ? src : MedicationSource.Manual,
                IsActive = true,
            };
            if (root.TryGetProperty("timeOfDay", out var tod))
                med.TimeOfDay = tod.EnumerateArray().Select(t => TimeOnly.Parse(t.GetString()!)).ToList();
            if (root.TryGetProperty("startDate", out var sd))
                med.StartDate = DateOnly.TryParse(sd.GetString(), out var d) ? d : null;
            if (root.TryGetProperty("endDate", out var ed))
                med.EndDate = DateOnly.TryParse(ed.GetString(), out var d2) ? d2 : null;
            if (root.TryGetProperty("notes", out var nt)) med.Notes = nt.GetString();
            db.Medications.Add(med);
            await db.SaveChangesAsync(ct);
            return Results.Ok(new { code = 0, data = new { med.Id }, message = (string?)null });
        });

        group.MapPut("/{id:guid}", async (Guid id, HttpRequest request, HttpContext http, AppDbContext db, CancellationToken ct) =>
        {
            var userId = GetUserId(http);
            var med = await db.Medications.FirstOrDefaultAsync(m => m.Id == id && m.UserId == userId, ct);
            if (med == null) return Results.Ok(new { code = 40004, message = "不存在" });
            request.EnableBuffering();
            using var reader = new StreamReader(request.Body);
            var body = await reader.ReadToEndAsync(ct);
            request.Body.Position = 0;
            using var json = JsonDocument.Parse(body);
            var root = json.RootElement;
            if (root.TryGetProperty("name", out var n)) med.Name = n.GetString()!;
            if (root.TryGetProperty("dosage", out var dg2)) med.Dosage = dg2.GetString();
            if (root.TryGetProperty("frequency", out var f2) && Enum.TryParse<MedicationFrequency>(f2.GetString(), out var freq2)) med.Frequency = freq2;
            if (root.TryGetProperty("timeOfDay", out var tod2)) med.TimeOfDay = tod2.EnumerateArray().Select(t => TimeOnly.Parse(t.GetString()!)).ToList();
            if (root.TryGetProperty("startDate", out var sd2) && DateOnly.TryParse(sd2.GetString(), out var d3)) med.StartDate = d3;
            if (root.TryGetProperty("endDate", out var ed2) && DateOnly.TryParse(ed2.GetString(), out var d4)) med.EndDate = d4;
            if (root.TryGetProperty("notes", out var nt2)) med.Notes = nt2.GetString();
            med.UpdatedAt = DateTime.UtcNow;
            await db.SaveChangesAsync(ct);
            return Results.Ok(new { code = 0, data = new { success = true }, message = (string?)null });
        });

        group.MapDelete("/{id:guid}", async (Guid id, HttpContext http, AppDbContext db, CancellationToken ct) =>
        {
            var userId = GetUserId(http);
            var med = await db.Medications.FirstOrDefaultAsync(m => m.Id == id && m.UserId == userId, ct);
            if (med != null) { db.Medications.Remove(med); await db.SaveChangesAsync(ct); }
            return Results.Ok(new { code = 0, data = new { success = true }, message = (string?)null });
        });

        group.MapPost("/{id:guid}/confirm", async (Guid id, HttpContext http, AppDbContext db, CancellationToken ct) =>
        {
            var userId = GetUserId(http);
            var today = DateOnly.FromDateTime(DateTime.UtcNow);
            var existing = await db.MedicationLogs.FirstOrDefaultAsync(l => l.MedicationId == id && l.CreatedAt.Date == DateTime.UtcNow.Date && l.Status == MedicationLogStatus.Taken, ct);
            if (existing != null) {
                db.MedicationLogs.Remove(existing);
                await db.SaveChangesAsync(ct);
                return Results.Ok(new { code = 0, data = new { taken = false }, message = (string?)null });
            }
            var log = new MedicationLog
            {
                Id = Guid.NewGuid(), MedicationId = id, UserId = userId,
                Status = MedicationLogStatus.Taken, ScheduledTime = TimeOnly.FromDateTime(DateTime.Now), ConfirmedAt = DateTime.UtcNow,
            };
            db.MedicationLogs.Add(log);
            await db.SaveChangesAsync(ct);
            return Results.Ok(new { code = 0, data = new { taken = true }, message = (string?)null });
        });

        group.MapGet("/reminders", async (HttpContext http, AppDbContext db, CancellationToken ct) =>
        {
            var userId = GetUserId(http);
            var now = TimeOnly.FromDateTime(DateTime.Now);
            var windowEnd = now.AddHours(1);
            var meds = await db.Medications
                .Where(m => m.UserId == userId && m.IsActive && m.TimeOfDay != null)
                .ToListAsync(ct);
            var due = meds.Where(m => m.TimeOfDay!.Any(t => t >= now && t <= windowEnd)).ToList();

            var today = DateOnly.FromDateTime(DateTime.Now);
            var dueMeds = new List<object>();
            foreach (var m in due)
            {
                var logged = await db.MedicationLogs.AnyAsync(l =>
                    l.MedicationId == m.Id && l.CreatedAt >= today.ToDateTime(TimeOnly.MinValue) && l.Status == MedicationLogStatus.Taken, ct);
                if (!logged)
                    dueMeds.Add(new { m.Id, m.Name, m.Dosage, m.TimeOfDay });
            }

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

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

