using System.Security.Claims;
using System.Text.Json;
using Health.Application.Notifications;
using Health.Domain.Enums;

namespace Health.WebApi.Endpoints;

public static class NotificationEndpoints
{
    public static void MapNotificationEndpoints(this WebApplication app)
    {
        var group = app.MapGroup("/api/notifications").RequireAuthorization();

        group.MapGet("/pending", async (HttpContext http, IInAppNotificationService notifications, CancellationToken ct) =>
        {
            var userId = GetUserId(http);
            if (userId == Guid.Empty) return Results.Unauthorized();
            var result = await notifications.GetPendingAsync(userId, ct);
            return Results.Ok(new { code = 0, data = result, message = (string?)null });
        });

        group.MapPost("/check-due", async (HttpContext http, IReminderCatchUpService reminders, CancellationToken ct) =>
        {
            var userId = GetUserId(http);
            if (userId == Guid.Empty) return Results.Unauthorized();
            var result = await reminders.CheckDueAsync(userId, DateTime.UtcNow, ct);
            return Results.Ok(new { code = 0, data = result, message = (string?)null });
        });

        group.MapGet("", async (HttpContext http, IInAppNotificationService notifications, CancellationToken ct) =>
        {
            var userId = GetUserId(http);
            if (userId == Guid.Empty) return Results.Unauthorized();
            var result = await notifications.GetHistoryAsync(userId, ct);
            return Results.Ok(new
            {
                code = 0,
                data = new { unreadCount = result.Count(x => !x.IsRead), items = result },
                message = (string?)null
            });
        });

        group.MapPost("/{id:guid}/acknowledge", async (Guid id, HttpContext http, IInAppNotificationService notifications, CancellationToken ct) =>
        {
            var userId = GetUserId(http);
            if (userId == Guid.Empty) return Results.Unauthorized();
            var acknowledged = await notifications.AcknowledgeAsync(userId, id, ct);
            return Results.Ok(new { code = 0, data = new { acknowledged }, message = (string?)null });
        });

        group.MapPost("/read-all", async (HttpContext http, IInAppNotificationService notifications, CancellationToken ct) =>
        {
            var userId = GetUserId(http);
            if (userId == Guid.Empty) return Results.Unauthorized();
            var count = await notifications.MarkAllReadAsync(userId, ct);
            return Results.Ok(new { code = 0, data = new { count }, message = (string?)null });
        });


        group.MapDelete("/{id:guid}", async (Guid id, HttpContext http, IInAppNotificationService notifications, CancellationToken ct) =>
        {
            var userId = GetUserId(http);
            if (userId == Guid.Empty) return Results.Unauthorized();
            var deleted = await notifications.DeleteAsync(userId, id, ct);
            return deleted
                ? Results.Ok(new { code = 0, data = new { deleted = true }, message = (string?)null })
                : Results.NotFound(new { code = 404, data = (object?)null, message = "通知不存在" });
        });

        // ── 通知偏好设置 ──
        var prefsGroup = app.MapGroup("/api/notification-prefs").RequireAuthorization();

        prefsGroup.MapGet("", async (HttpContext http, AppDbContext db, CancellationToken ct) =>
        {
            var userId = GetUserId(http);
            if (userId == Guid.Empty) return Results.Unauthorized();
            var pref = await db.NotificationPreferences.FirstOrDefaultAsync(p => p.UserId == userId, ct);
            if (pref == null)
            {
                pref = new NotificationPreference { Id = Guid.NewGuid(), UserId = userId };
                db.NotificationPreferences.Add(pref);
                await db.SaveChangesAsync(ct);
            }
            return Results.Ok(new { code = 0, data = ToDto(pref), message = (string?)null });
        });

        prefsGroup.MapPut("", async (NotificationPrefsUpdateDto body, HttpContext http, AppDbContext db, CancellationToken ct) =>
        {
            var userId = GetUserId(http);
            if (userId == Guid.Empty) return Results.Unauthorized();
            var pref = await db.NotificationPreferences.FirstOrDefaultAsync(p => p.UserId == userId, ct);
            if (pref == null)
            {
                pref = new NotificationPreference { Id = Guid.NewGuid(), UserId = userId };
                db.NotificationPreferences.Add(pref);
            }
            if (body.MedicationReminder.HasValue) pref.MedicationReminder = body.MedicationReminder.Value;
            if (body.FollowUpReminder.HasValue) pref.FollowUpReminder = body.FollowUpReminder.Value;
            if (body.DoctorReply.HasValue) pref.DoctorReply = body.DoctorReply.Value;
            if (body.AbnormalAlert.HasValue) pref.AbnormalAlert = body.AbnormalAlert.Value;
            if (body.PushEnabled.HasValue) pref.PushEnabled = body.PushEnabled.Value;
            if (body.DndEnabled.HasValue) pref.DndEnabled = body.DndEnabled.Value;
            if (body.DndStartMinutes is >= 0 and < 1440) pref.DndStartMinutes = body.DndStartMinutes.Value;
            if (body.DndEndMinutes is >= 0 and < 1440) pref.DndEndMinutes = body.DndEndMinutes.Value;
            if (body.HealthRecordReminder.HasValue) pref.HealthRecordReminder = body.HealthRecordReminder.Value;
            if (body.HealthRecordReminderBloodPressure.HasValue) pref.HealthRecordReminderBloodPressure = body.HealthRecordReminderBloodPressure.Value;
            if (body.HealthRecordReminderHeartRate.HasValue) pref.HealthRecordReminderHeartRate = body.HealthRecordReminderHeartRate.Value;
            if (body.HealthRecordReminderGlucose.HasValue) pref.HealthRecordReminderGlucose = body.HealthRecordReminderGlucose.Value;
            if (body.HealthRecordReminderSpO2.HasValue) pref.HealthRecordReminderSpO2 = body.HealthRecordReminderSpO2.Value;
            if (body.HealthRecordReminderWeight.HasValue) pref.HealthRecordReminderWeight = body.HealthRecordReminderWeight.Value;
            pref.UpdatedAt = DateTime.UtcNow;
            await db.SaveChangesAsync(ct);

            // 保存后立即检查该用户是否需要健康录入提醒，不用等到 9:00/12:00
            if (body.HealthRecordReminder is true && pref.PushEnabled && pref.HealthRecordReminder)
            {
                var nowCst = DateTime.UtcNow.AddHours(8);
                var todayStart = nowCst.Date.AddHours(-8);
                var todayEnd = todayStart.AddDays(1);

                var recordedTypes = await db.HealthRecords
                    .Where(r => r.UserId == userId && r.RecordedAt >= todayStart && r.RecordedAt < todayEnd)
                    .Select(r => r.MetricType)
                    .Distinct()
                    .ToListAsync(ct);

                var missing = new List<(HealthMetricType type, bool enabled, string label)>
                {
                    (HealthMetricType.BloodPressure, pref.HealthRecordReminderBloodPressure, "血压"),
                    (HealthMetricType.HeartRate, pref.HealthRecordReminderHeartRate, "心率"),
                    (HealthMetricType.Glucose, pref.HealthRecordReminderGlucose, "血糖"),
                    (HealthMetricType.SpO2, pref.HealthRecordReminderSpO2, "血氧"),
                    (HealthMetricType.Weight, pref.HealthRecordReminderWeight, "体重"),
                };

                var missingLabels = missing
                    .Where(m => m.enabled && !recordedTypes.Contains(m.type))
                    .Select(m => m.label)
                    .ToList();

                if (missingLabels.Count > 0)
                {
                    var producer = http.RequestServices.GetRequiredService<IUserNotificationProducer>();
                    var sourceId = Guid.NewGuid();
                    await producer.EnqueueAsync(userId, sourceId, new NotificationMessage(
                        Type: "health_record_reminder",
                        Title: "记录今日健康指标",
                        Message: $"建议录入：{string.Join("、", missingLabels)}",
                        Severity: "info",
                        ActionType: "health",
                        ActionTargetId: null), ct);
                }
            }

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

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

    private static object ToDto(NotificationPreference pref) => new
    {
        medicationReminder = pref.MedicationReminder,
        followUpReminder = pref.FollowUpReminder,
        doctorReply = pref.DoctorReply,
        abnormalAlert = pref.AbnormalAlert,
        pushEnabled = pref.PushEnabled,
        dndEnabled = pref.DndEnabled,
        dndStartMinutes = pref.DndStartMinutes,
        dndEndMinutes = pref.DndEndMinutes,
        healthRecordReminder = pref.HealthRecordReminder,
        healthRecordReminderBloodPressure = pref.HealthRecordReminderBloodPressure,
        healthRecordReminderHeartRate = pref.HealthRecordReminderHeartRate,
        healthRecordReminderGlucose = pref.HealthRecordReminderGlucose,
        healthRecordReminderSpO2 = pref.HealthRecordReminderSpO2,
        healthRecordReminderWeight = pref.HealthRecordReminderWeight,
    };
}

public sealed record NotificationPrefsUpdateDto(
    bool? MedicationReminder,
    bool? FollowUpReminder,
    bool? DoctorReply,
    bool? AbnormalAlert,
    bool? PushEnabled,
    bool? DndEnabled,
    int? DndStartMinutes,
    int? DndEndMinutes,
    bool? HealthRecordReminder,
    bool? HealthRecordReminderBloodPressure,
    bool? HealthRecordReminderHeartRate,
    bool? HealthRecordReminderGlucose,
    bool? HealthRecordReminderSpO2,
    bool? HealthRecordReminderWeight
);
