public record MimicPatient(int SubjectId, string Gender, int AnchorAge, int AnchorYear, string? Dod);

public record MimicAdmission(
    int SubjectId, int HadmId,
    DateTime AdmitTime, DateTime DischTime, DateTime? DeathTime,
    string AdmissionType, string? AdmissionLocation, string? DischargeLocation,
    int HospitalExpireFlag);

public record MimicIcuStay(
    int SubjectId, int HadmId, int StayId,
    string FirstCareUnit, string LastCareUnit,
    DateTime InTime, DateTime OutTime, decimal Los);

public record MimicChartEvent(
    int StayId, DateTime ChartTime, int ItemId,
    string? TextValue, decimal? ValueNum);

public record MimicLabEvent(
    int HadmId, DateTime ChartTime, int ItemId,
    decimal? ValueNum, string? ValueUom);

public record MimicPrescription(
    int HadmId, DateTime StartTime,
    string Drug, string? DoseValRx, string? DoseUnitRx, string? Route);

public class MimicDataLoader
{
    private readonly string _dataDir;

    public MimicDataLoader(string dataDir)
    {
        if (!Directory.Exists(dataDir))
            throw new DirectoryNotFoundException($"MIMIC data directory not found: {dataDir}");
        _dataDir = dataDir;
    }

    private string Path(string fileName) => System.IO.Path.Combine(_dataDir, fileName);

    public List<MimicPatient> LoadPatients()
    {
        return MimicCsvReader.ReadAll(Path("patients.csv"), (f, h) =>
        {
            var id = MimicCsvReader.ColInt(f, h, "subject_id");
            var age = MimicCsvReader.ColInt(f, h, "anchor_age");
            var year = MimicCsvReader.ColInt(f, h, "anchor_year");
            if (id is null || age is null || year is null) return null;
            return new MimicPatient(
                id.Value,
                MimicCsvReader.Col(f, h, "gender"),
                age.Value,
                year.Value,
                MimicCsvReader.Col(f, h, "dod") is { Length: > 0 } dod ? dod : null);
        });
    }

    public List<MimicAdmission> LoadAdmissions()
    {
        return MimicCsvReader.ReadAll(Path("admissions.csv"), (f, h) =>
        {
            var subjectId = MimicCsvReader.ColInt(f, h, "subject_id");
            var hadmId = MimicCsvReader.ColInt(f, h, "hadm_id");
            var admitTime = MimicCsvReader.ColDateTime(f, h, "admittime");
            var dischTime = MimicCsvReader.ColDateTime(f, h, "dischtime");
            if (subjectId is null || hadmId is null || admitTime is null || dischTime is null)
                return null;
            return new MimicAdmission(
                subjectId.Value, hadmId.Value,
                admitTime.Value, dischTime.Value,
                MimicCsvReader.ColDateTime(f, h, "deathtime"),
                MimicCsvReader.Col(f, h, "admission_type"),
                MimicCsvReader.Col(f, h, "admission_location") is { Length: > 0 } loc ? loc : null,
                MimicCsvReader.Col(f, h, "discharge_location") is { Length: > 0 } dloc ? dloc : null,
                MimicCsvReader.ColInt(f, h, "hospital_expire_flag") ?? 0);
        });
    }

    public List<MimicIcuStay> LoadIcuStays()
    {
        return MimicCsvReader.ReadAll(Path("icustays.csv"), (f, h) =>
        {
            var subjectId = MimicCsvReader.ColInt(f, h, "subject_id");
            var hadmId = MimicCsvReader.ColInt(f, h, "hadm_id");
            var stayId = MimicCsvReader.ColInt(f, h, "stay_id");
            var inTime = MimicCsvReader.ColDateTime(f, h, "intime");
            var outTime = MimicCsvReader.ColDateTime(f, h, "outtime");
            if (subjectId is null || hadmId is null || stayId is null
                || inTime is null || outTime is null)
                return null;
            return new MimicIcuStay(
                subjectId.Value, hadmId.Value, stayId.Value,
                MimicCsvReader.Col(f, h, "first_careunit"),
                MimicCsvReader.Col(f, h, "last_careunit"),
                inTime.Value, outTime.Value,
                MimicCsvReader.ColDecimal(f, h, "los") ?? 0m);
        });
    }

    public IEnumerable<MimicChartEvent> StreamChartEvents(int stayId)
    {
        var stayIdStr = stayId.ToString();
        return MimicCsvReader.Read(
            Path("chartevents.csv"),
            parser: (f, h) =>
            {
                var itemId = MimicCsvReader.ColInt(f, h, "itemid");
                if (itemId is null || !MimicItemMap.AllChartItemIds.Contains(itemId.Value))
                    return null;

                var chartTime = MimicCsvReader.ColDateTime(f, h, "charttime");
                if (chartTime is null) return null;

                var valueNum = MimicCsvReader.ColDecimal(f, h, "valuenum");
                var textValue = MimicCsvReader.Col(f, h, "value");

                if (MimicItemMap.IsGcsItem(itemId.Value))
                {
                    var gcsVal = MimicItemMap.ResolveGcsValue(itemId.Value, textValue, valueNum);
                    if (gcsVal is null) return null;
                    return new MimicChartEvent(stayId, chartTime.Value, itemId.Value, textValue, gcsVal);
                }

                if (valueNum is null) return null;

                return new MimicChartEvent(stayId, chartTime.Value, itemId.Value, textValue, valueNum);
            },
            filter: (f, h) =>
            {
                var sid = MimicCsvReader.Col(f, h, "stay_id");
                return sid == stayIdStr;
            });
    }

    public IEnumerable<MimicLabEvent> StreamLabEvents(int hadmId, DateTime? after = null, DateTime? before = null)
    {
        var hadmIdStr = hadmId.ToString();
        return MimicCsvReader.Read(
            Path("labevents.csv"),
            parser: (f, h) =>
            {
                var itemId = MimicCsvReader.ColInt(f, h, "itemid");
                if (itemId is null || !MimicItemMap.AllLabItemIds.Contains(itemId.Value))
                    return null;

                var chartTime = MimicCsvReader.ColDateTime(f, h, "charttime");
                if (chartTime is null) return null;
                if (after.HasValue && chartTime.Value < after.Value) return null;
                if (before.HasValue && chartTime.Value > before.Value) return null;

                var valueNum = MimicCsvReader.ColDecimal(f, h, "valuenum");
                if (valueNum is null) return null;

                return new MimicLabEvent(
                    hadmId, chartTime.Value, itemId.Value, valueNum,
                    MimicCsvReader.Col(f, h, "valueuom") is { Length: > 0 } uom ? uom : null);
            },
            filter: (f, h) =>
            {
                var hid = MimicCsvReader.Col(f, h, "hadm_id");
                return hid == hadmIdStr;
            });
    }

    public IEnumerable<MimicPrescription> StreamPrescriptions(
        int hadmId, DateTime? after = null, DateTime? before = null)
    {
        var hadmIdStr = hadmId.ToString();
        return MimicCsvReader.Read(
            Path("prescriptions.csv"),
            parser: (f, h) =>
            {
                var startTime = MimicCsvReader.ColDateTime(f, h, "starttime");
                if (startTime is null) return null;
                if (after.HasValue && startTime.Value < after.Value) return null;
                if (before.HasValue && startTime.Value > before.Value) return null;

                var drug = MimicCsvReader.Col(f, h, "drug");
                if (string.IsNullOrWhiteSpace(drug)) return null;

                return new MimicPrescription(
                    hadmId, startTime.Value, drug,
                    MimicCsvReader.Col(f, h, "dose_val_rx") is { Length: > 0 } d ? d : null,
                    MimicCsvReader.Col(f, h, "dose_unit_rx") is { Length: > 0 } u ? u : null,
                    MimicCsvReader.Col(f, h, "route") is { Length: > 0 } r ? r : null);
            },
            filter: (f, h) =>
            {
                var hid = MimicCsvReader.Col(f, h, "hadm_id");
                return hid == hadmIdStr;
            });
    }
}
