using Health.Domain.Entities;
using Health.Domain.Enums;
using Health.Application.Notifications;
using System.Security.Cryptography;
using System.Text;

namespace Health.Application.HealthRecords;

public sealed class HealthRecordService(
    IHealthRecordRepository records,
    IUserNotificationProducer? notifications = null) : IHealthRecordService
{
    private readonly IHealthRecordRepository _records = records;
    private readonly IUserNotificationProducer? _notifications = notifications;

    public async Task<IReadOnlyList<HealthRecordDto>> GetRecordsAsync(Guid userId, string? type, int? days, CancellationToken ct)
    {
        HealthMetricType? metricType = null;
        if (!string.IsNullOrEmpty(type) && Enum.TryParse<HealthMetricType>(type, ignoreCase: true, out var parsed))
            metricType = parsed;

        var recordedAfter = days.HasValue ? DateTime.UtcNow.AddDays(-days.Value) : (DateTime?)null;
        var result = await _records.ListAsync(userId, metricType, recordedAfter, 100, ct);
        return result.Select(ToDto).ToList();
    }

    public async Task<Guid> CreateAsync(Guid userId, HealthRecordUpsertRequest request, CancellationToken ct)
    {
        HealthRecordRules.Validate(request);

        var record = new HealthRecord
        {
            Id = Guid.NewGuid(),
            UserId = userId,
            MetricType = request.Type,
            Systolic = request.Systolic,
            Diastolic = request.Diastolic,
            Value = request.Value,
            Unit = request.Unit,
            Source = request.Source,
            RecordedAt = request.RecordedAt ?? DateTime.UtcNow,
            CreatedAt = DateTime.UtcNow,
            IsAbnormal = HealthRecordRules.CheckAbnormal(request),
        };

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

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

        HealthRecordRules.Validate(request);

        record.MetricType = request.Type;
        record.Systolic = request.Systolic;
        record.Diastolic = request.Diastolic;
        record.Value = request.Value;
        record.Unit = request.Unit;
        record.Source = request.Source;
        record.RecordedAt = request.RecordedAt ?? record.RecordedAt;
        record.IsAbnormal = HealthRecordRules.CheckAbnormal(request);

        await _records.SaveChangesAsync(ct);
        await EnqueueAbnormalNotificationAsync(record, ct);
        return true;
    }

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

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

    public async Task<Dictionary<string, object?>> GetLatestAsync(Guid userId, CancellationToken ct)
    {
        var types = new[]
        {
            HealthMetricType.BloodPressure,
            HealthMetricType.HeartRate,
            HealthMetricType.Glucose,
            HealthMetricType.SpO2,
            HealthMetricType.Weight
        };

        var result = new Dictionary<string, object?>();
        foreach (var type in types)
        {
            var latest = await _records.GetLatestByTypeAsync(userId, type, ct);
            result[type.ToString()] = latest == null ? null : new
            {
                latest.Systolic,
                latest.Diastolic,
                latest.Value,
                latest.Unit,
                latest.RecordedAt
            };
        }

        return result;
    }

    public async Task<IReadOnlyList<object>> GetTrendAsync(Guid userId, HealthMetricType type, int period, CancellationToken ct)
    {
        var days = period switch { 7 => 7, 30 => 30, 90 => 90, 365 => 365, _ => 7 };
        var records = await _records.GetTrendAsync(userId, type, DateTime.UtcNow.AddDays(-days), ct);
        return records.Select(r => new { r.Id, r.Systolic, r.Diastolic, r.Value, r.Unit, r.IsAbnormal, r.RecordedAt }).Cast<object>().ToList();
    }

    public static HealthRecordDto ToDto(HealthRecord record) => new(
        record.Id,
        record.MetricType.ToString(),
        record.Systolic,
        record.Diastolic,
        record.Value,
        record.Unit,
        record.Source.ToString(),
        record.IsAbnormal,
        record.RecordedAt);

    private async Task EnqueueAbnormalNotificationAsync(HealthRecord record, CancellationToken ct)
    {
        if (!record.IsAbnormal || _notifications == null) return;

        var (title, message, severity, target) = record.MetricType switch
        {
            HealthMetricType.BloodPressure when record.Systolic >= 140 || record.Diastolic >= 90 =>
                ("血压偏高提醒", $"本次血压为 {record.Systolic}/{record.Diastolic} mmHg，高于参考范围。建议休息后复测；如伴明显不适，请及时就医。", "warning", "blood_pressure"),
            HealthMetricType.BloodPressure =>
                ("血压偏低提醒", $"本次血压为 {record.Systolic}/{record.Diastolic} mmHg，低于参考范围。请留意头晕、乏力等不适，必要时及时就医。", "warning", "blood_pressure"),
            HealthMetricType.HeartRate when record.Value > 100 =>
                ("心率偏高提醒", $"本次心率为 {record.Value:0.#} 次/分，高于参考范围。建议安静休息后复测。", "warning", "heart_rate"),
            HealthMetricType.HeartRate =>
                ("心率偏低提醒", $"本次心率为 {record.Value:0.#} 次/分，低于参考范围。如伴明显不适，请及时就医。", "warning", "heart_rate"),
            HealthMetricType.Glucose when record.Value >= 7.0m =>
                ("血糖偏高提醒", $"本次血糖为 {record.Value:0.#} mmol/L，高于参考范围。请结合测量时段并按计划复测。", "warning", "glucose"),
            HealthMetricType.Glucose =>
                ("血糖偏低提醒", $"本次血糖为 {record.Value:0.#} mmol/L，低于参考范围。请及时关注身体状况。", "critical", "glucose"),
            HealthMetricType.SpO2 =>
                ("血氧偏低提醒", $"本次血氧为 {record.Value:0.#}%，低于参考范围。建议立即复测；如持续偏低或伴呼吸不适，请及时就医。", "critical", "spo2"),
            _ => ("健康指标提醒", "检测到一项健康指标超出参考范围，请查看详情。", "warning", "")
        };

        var window = DateTime.UtcNow.Ticks / TimeSpan.FromMinutes(30).Ticks;
        var sourceId = DeterministicGuid($"{record.UserId}:{record.MetricType}:{severity}:{window}");
        await _notifications.EnqueueAsync(
            record.UserId,
            sourceId,
            new NotificationMessage("HealthMetricAlert", title, message, severity, "health", target),
            ct);
    }

    private static Guid DeterministicGuid(string value)
    {
        var hash = SHA256.HashData(Encoding.UTF8.GetBytes(value));
        return new Guid(hash.AsSpan(0, 16));
    }
}
