using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using StackExchange.Redis;

public static class DataSeeder
{
    public static async Task SeedAsync(AppDbContext db, IConnectionMultiplexer redis)
    {
        if (await db.Patients.AnyAsync()) return;

        // Two patients
        var patient1 = new Patient
        {
            Id = Guid.NewGuid(), Mrn = "MRN-000001", FirstName = "Jane", LastName = "Smith",
            DateOfBirth = new DateOnly(1975, 4, 12), Gender = "F",
            BloodType = BloodType.APositive, Allergies = "Penicillin",
            EmergencyContactName = "John Smith", EmergencyContactPhone = "555-0101",
            CreatedAt = DateTimeOffset.UtcNow
        };
        var patient2 = new Patient
        {
            Id = Guid.NewGuid(), Mrn = "MRN-000002", FirstName = "Robert", LastName = "Chen",
            DateOfBirth = new DateOnly(1962, 9, 3), Gender = "M",
            BloodType = BloodType.ONegative, Allergies = null,
            EmergencyContactName = "Linda Chen", EmergencyContactPhone = "555-0202",
            CreatedAt = DateTimeOffset.UtcNow
        };
        db.Patients.AddRange(patient1, patient2);

        // One active inpatient encounter per patient
        var encounter1 = new Encounter
        {
            Id = Guid.NewGuid(), PatientId = patient1.Id, EncounterType = EncounterType.Inpatient,
            Status = EncounterStatus.Active, Department = Department.Icu,
            AttendingPhysician = "Dr. Osei",
            RoomBed = "ICU-4B", AdmissionReason = "Chest pain, rule out MI",
            AdmittedAt = DateTimeOffset.UtcNow.AddHours(-6),
            CreatedAt = DateTimeOffset.UtcNow.AddHours(-6)
        };
        var encounter2 = new Encounter
        {
            Id = Guid.NewGuid(), PatientId = patient2.Id, EncounterType = EncounterType.Inpatient,
            Status = EncounterStatus.Active, Department = Department.GeneralMedicine,
            AttendingPhysician = "Dr. Patel",
            RoomBed = "GM-12A", AdmissionReason = "Pneumonia, fever for 3 days",
            AdmittedAt = DateTimeOffset.UtcNow.AddHours(-12),
            CreatedAt = DateTimeOffset.UtcNow.AddHours(-12)
        };
        db.Encounters.AddRange(encounter1, encounter2);

        var thresholds = BuildDefaultThresholds();
        db.AlertThresholds.AddRange(thresholds);

        // Observations spanning normal, warning, and critical ranges for encounter1
        var now = DateTimeOffset.UtcNow;
        var observations = new List<Observation>
        {
            // Normal heart rate
            new() { Id = Guid.NewGuid(), EncounterId = encounter1.Id, ObservationCode = "HEART_RATE",
                Value = 78, Unit = "bpm", Source = ObservationSource.Device, RecordedAt = now.AddMinutes(-30), CreatedAt = now.AddMinutes(-30) },
            // Warning heart rate
            new() { Id = Guid.NewGuid(), EncounterId = encounter1.Id, ObservationCode = "HEART_RATE",
                Value = 104, Unit = "bpm", Source = ObservationSource.Device, RecordedAt = now.AddMinutes(-15), CreatedAt = now.AddMinutes(-15) },
            // Critical potassium (low)
            new() { Id = Guid.NewGuid(), EncounterId = encounter1.Id, ObservationCode = "POTASSIUM_MEQ_L",
                Value = 2.3m, Unit = "mEq/L", Source = ObservationSource.Lab, RecordedAt = now.AddMinutes(-10), CreatedAt = now.AddMinutes(-10) },
            // Normal SpO2
            new() { Id = Guid.NewGuid(), EncounterId = encounter1.Id, ObservationCode = "SPO2",
                Value = 97, Unit = "%", Source = ObservationSource.Device, RecordedAt = now.AddMinutes(-5), CreatedAt = now.AddMinutes(-5) },
            // Normal temp for encounter2
            new() { Id = Guid.NewGuid(), EncounterId = encounter2.Id, ObservationCode = "TEMP_C",
                Value = 37.1m, Unit = "°C", Source = ObservationSource.Manual, RecordedAt = now.AddMinutes(-20), CreatedAt = now.AddMinutes(-20) },
            // Blood pressure
            new() { Id = Guid.NewGuid(), EncounterId = encounter1.Id, ObservationCode = "SYSTOLIC_BP",
                Value = 128, Unit = "mmHg", Source = ObservationSource.Device, RecordedAt = now.AddMinutes(-25), CreatedAt = now.AddMinutes(-25) },
            new() { Id = Guid.NewGuid(), EncounterId = encounter1.Id, ObservationCode = "DIASTOLIC_BP",
                Value = 82, Unit = "mmHg", Source = ObservationSource.Device, RecordedAt = now.AddMinutes(-25), CreatedAt = now.AddMinutes(-25) },
            // Normal lactate
            new() { Id = Guid.NewGuid(), EncounterId = encounter1.Id, ObservationCode = "LACTATE_MMOL_L",
                Value = 1.2m, Unit = "mmol/L", Source = ObservationSource.Lab, RecordedAt = now.AddMinutes(-8), CreatedAt = now.AddMinutes(-8) },
            // AVPU = Alert (normal consciousness)
            new() { Id = Guid.NewGuid(), EncounterId = encounter1.Id, ObservationCode = "AVPU",
                Value = 0, Unit = "score", Source = ObservationSource.Manual, RecordedAt = now.AddMinutes(-28), CreatedAt = now.AddMinutes(-28) },
            new() { Id = Guid.NewGuid(), EncounterId = encounter2.Id, ObservationCode = "AVPU",
                Value = 0, Unit = "score", Source = ObservationSource.Manual, RecordedAt = now.AddMinutes(-18), CreatedAt = now.AddMinutes(-18) },
            // Room air (no supplemental oxygen)
            new() { Id = Guid.NewGuid(), EncounterId = encounter1.Id, ObservationCode = "SUPPLEMENTAL_O2",
                Value = 0, Unit = "flag", Source = ObservationSource.Manual, RecordedAt = now.AddMinutes(-27), CreatedAt = now.AddMinutes(-27) },
            new() { Id = Guid.NewGuid(), EncounterId = encounter2.Id, ObservationCode = "SUPPLEMENTAL_O2",
                Value = 1, Unit = "flag", Source = ObservationSource.Manual, RecordedAt = now.AddMinutes(-17), CreatedAt = now.AddMinutes(-17) },
        };
        db.Observations.AddRange(observations);

        await db.SaveChangesAsync();

        await db.Database.ExecuteSqlRawAsync(
            "SELECT setval('mrn_seq', COALESCE((SELECT MAX(CAST(SUBSTRING(mrn FROM 5) AS bigint)) FROM patients WHERE mrn ~ '^MRN-[0-9]+$'), 0))");

        await CacheThresholdsAsync(redis, thresholds);
    }

    public static async Task SeedThresholdsOnlyAsync(AppDbContext db, IConnectionMultiplexer redis)
    {
        await db.Database.ExecuteSqlRawAsync("DELETE FROM alert_thresholds");
        var thresholds = BuildDefaultThresholds();
        db.AlertThresholds.AddRange(thresholds);
        await db.SaveChangesAsync();
        await CacheThresholdsAsync(redis, thresholds);
    }

    public static List<AlertThreshold> BuildDefaultThresholds() => new()
    {
        new() {
            Id = Guid.NewGuid(), ObservationCode = "HEART_RATE", DisplayName = "Heart Rate",
            Unit = "bpm", CriticalLow = 30, WarningLow = 50, WarningHigh = 100, CriticalHigh = 150,
            CreatedAt = DateTimeOffset.UtcNow
        },
        new() {
            Id = Guid.NewGuid(), ObservationCode = "TEMP_C", DisplayName = "Body Temperature",
            Unit = "°C", CriticalLow = 35.0m, WarningLow = 36.0m, WarningHigh = 38.3m, CriticalHigh = 40.0m,
            CreatedAt = DateTimeOffset.UtcNow
        },
        new() {
            Id = Guid.NewGuid(), ObservationCode = "POTASSIUM_MEQ_L", DisplayName = "Serum Potassium",
            Unit = "mEq/L", CriticalLow = 2.5m, WarningLow = 3.5m, WarningHigh = 5.0m, CriticalHigh = 6.5m,
            CreatedAt = DateTimeOffset.UtcNow
        },
        new() {
            Id = Guid.NewGuid(), ObservationCode = "SPO2", DisplayName = "Oxygen Saturation",
            Unit = "%", CriticalLow = 88, WarningLow = 92, WarningHigh = null, CriticalHigh = null,
            CreatedAt = DateTimeOffset.UtcNow
        },
        new AlertThreshold
        {
            Id = Guid.NewGuid(), ObservationCode = "RESP_RATE",
            DisplayName = "Respiratory Rate", Unit = "breaths/min",
            CriticalLow = null, WarningLow = 12m, WarningHigh = 20m, CriticalHigh = 30m,
            CreatedAt = DateTimeOffset.UtcNow
        },
        new AlertThreshold
        {
            Id = Guid.NewGuid(), ObservationCode = "WBC_K_UL",
            DisplayName = "White Blood Cell Count", Unit = "k/µL",
            CriticalLow = 2.0m, WarningLow = 4.0m, WarningHigh = 12.0m, CriticalHigh = 20.0m,
            CreatedAt = DateTimeOffset.UtcNow
        },
        new AlertThreshold
        {
            Id = Guid.NewGuid(), ObservationCode = "SYSTOLIC_BP",
            DisplayName = "Systolic Blood Pressure", Unit = "mmHg",
            CriticalLow = 70m, WarningLow = 90m, WarningHigh = 160m, CriticalHigh = 180m,
            CreatedAt = DateTimeOffset.UtcNow
        },
        new AlertThreshold
        {
            Id = Guid.NewGuid(), ObservationCode = "DIASTOLIC_BP",
            DisplayName = "Diastolic Blood Pressure", Unit = "mmHg",
            CriticalLow = 40m, WarningLow = 60m, WarningHigh = 90m, CriticalHigh = 110m,
            CreatedAt = DateTimeOffset.UtcNow
        },
        new AlertThreshold
        {
            Id = Guid.NewGuid(), ObservationCode = "LACTATE_MMOL_L",
            DisplayName = "Serum Lactate", Unit = "mmol/L",
            CriticalLow = null, WarningLow = null, WarningHigh = 2.0m, CriticalHigh = 4.0m,
            CreatedAt = DateTimeOffset.UtcNow
        },
        new AlertThreshold
        {
            Id = Guid.NewGuid(), ObservationCode = "AVPU",
            DisplayName = "AVPU Consciousness Level", Unit = "score",
            CriticalLow = null, WarningLow = null, WarningHigh = null, CriticalHigh = 2m,
            CreatedAt = DateTimeOffset.UtcNow
        },
        new AlertThreshold
        {
            Id = Guid.NewGuid(), ObservationCode = "SUPPLEMENTAL_O2",
            DisplayName = "Supplemental Oxygen", Unit = "flag",
            CriticalLow = null, WarningLow = null, WarningHigh = null, CriticalHigh = null,
            CreatedAt = DateTimeOffset.UtcNow
        },
        new AlertThreshold
        {
            Id = Guid.NewGuid(), ObservationCode = "GLUCOSE_MG_DL",
            DisplayName = "Blood Glucose", Unit = "mg/dL",
            CriticalLow = 40m, WarningLow = 70m, WarningHigh = 180m, CriticalHigh = 400m,
            CreatedAt = DateTimeOffset.UtcNow
        },
        new AlertThreshold
        {
            Id = Guid.NewGuid(), ObservationCode = "GCS_EYE",
            DisplayName = "GCS Eye Response", Unit = "score",
            CriticalLow = null, WarningLow = null, WarningHigh = null, CriticalHigh = null,
            CreatedAt = DateTimeOffset.UtcNow
        },
        new AlertThreshold
        {
            Id = Guid.NewGuid(), ObservationCode = "GCS_VERBAL",
            DisplayName = "GCS Verbal Response", Unit = "score",
            CriticalLow = null, WarningLow = null, WarningHigh = null, CriticalHigh = null,
            CreatedAt = DateTimeOffset.UtcNow
        },
        new AlertThreshold
        {
            Id = Guid.NewGuid(), ObservationCode = "GCS_MOTOR",
            DisplayName = "GCS Motor Response", Unit = "score",
            CriticalLow = null, WarningLow = null, WarningHigh = null, CriticalHigh = null,
            CreatedAt = DateTimeOffset.UtcNow
        },
        new AlertThreshold
        {
            Id = Guid.NewGuid(), ObservationCode = "PAO2_MMHG",
            DisplayName = "Partial Pressure O2 (Arterial)", Unit = "mmHg",
            CriticalLow = 60m, WarningLow = 80m, WarningHigh = null, CriticalHigh = null,
            CreatedAt = DateTimeOffset.UtcNow
        },
        new AlertThreshold
        {
            Id = Guid.NewGuid(), ObservationCode = "FIO2_PCT",
            DisplayName = "Fraction of Inspired O2", Unit = "%",
            CriticalLow = null, WarningLow = null, WarningHigh = null, CriticalHigh = null,
            CreatedAt = DateTimeOffset.UtcNow
        },
        new AlertThreshold
        {
            Id = Guid.NewGuid(), ObservationCode = "PLATELET_K_UL",
            DisplayName = "Platelet Count", Unit = "k/µL",
            CriticalLow = 20m, WarningLow = 50m, WarningHigh = null, CriticalHigh = null,
            CreatedAt = DateTimeOffset.UtcNow
        },
        new AlertThreshold
        {
            Id = Guid.NewGuid(), ObservationCode = "BILIRUBIN_MG_DL",
            DisplayName = "Total Bilirubin", Unit = "mg/dL",
            CriticalLow = null, WarningLow = null, WarningHigh = 2.0m, CriticalHigh = 6.0m,
            CreatedAt = DateTimeOffset.UtcNow
        },
        new AlertThreshold
        {
            Id = Guid.NewGuid(), ObservationCode = "CREATININE_MG_DL",
            DisplayName = "Serum Creatinine", Unit = "mg/dL",
            CriticalLow = null, WarningLow = null, WarningHigh = 2.0m, CriticalHigh = 3.5m,
            CreatedAt = DateTimeOffset.UtcNow
        },
        new AlertThreshold
        {
            Id = Guid.NewGuid(), ObservationCode = "URINE_OUTPUT_ML_H",
            DisplayName = "Urine Output", Unit = "mL/h",
            CriticalLow = null, WarningLow = null, WarningHigh = null, CriticalHigh = null,
            CreatedAt = DateTimeOffset.UtcNow
        },
    };

    private static async Task CacheThresholdsAsync(
        IConnectionMultiplexer redis, IEnumerable<AlertThreshold> thresholds)
    {
        var cache = redis.GetDatabase();
        foreach (var t in thresholds)
        {
            var json = JsonSerializer.Serialize(new
            {
                t.ObservationCode, t.CriticalLow, t.WarningLow, t.WarningHigh, t.CriticalHigh
            });
            await cache.StringSetAsync($"threshold:{t.ObservationCode}", json);
        }
    }
}