-- ------------------------------------------------------------------
-- Title: Suspicion of Infection
--
-- Adapted from the MIMIC-IV upstream
--   concepts/sepsis/suspicion_of_infection.sql
-- and ported to MIMIC-III v1.3 vanilla PostgreSQL.
--
-- Definition (from the original Sepsis-3 paper, Seymour 2016):
-- a patient is "suspected of infection" if a culture and an
-- antibiotic are ordered close in time:
--   - culture <= 72 h before antibiotic, OR
--   - culture <= 24 h after antibiotic.
-- The antibiotic time is taken as the suspected-infection time when
-- a culture comes second; the culture time when it comes first.
--
-- PORT NOTES:
--   1. ID column is `icustay_id`, not `stay_id`.
--   2. MIMIC-III has no `micro_specimen_id`; specimens are identified
--      by the tuple (subject_id, hadm_id, chartdate, charttime,
--      spec_itemid, spec_type_desc) and we deduplicate organism
--      rows by aggregating with that tuple.
--   3. MIMIC-III `prescriptions.startdate` is DATE-precision only.
--      Consequently `antibiotic_time` always lands on midnight; the
--      MIMIC-IV branches that compare to `me.charttime` still work
--      (DATE auto-casts to TIMESTAMP at 00:00) but give day-level
--      onset precision.
-- ------------------------------------------------------------------

DROP TABLE IF EXISTS suspicion_of_infection;
CREATE TABLE suspicion_of_infection AS

WITH ab_tbl AS
(
  SELECT abx.subject_id
       , abx.hadm_id
       , abx.icustay_id
       , abx.antibiotic
       , CAST(abx.starttime AS TIMESTAMP) AS antibiotic_time
       , CAST(abx.starttime AS DATE)      AS antibiotic_date
       , CAST(abx.stoptime  AS TIMESTAMP) AS antibiotic_stoptime
       , ROW_NUMBER() OVER (
           PARTITION BY abx.subject_id
           ORDER BY     abx.starttime, abx.stoptime, abx.antibiotic
         ) AS ab_id
  FROM   antibiotic abx
)
, me AS
(
  SELECT subject_id, hadm_id
       , spec_itemid
       , spec_type_desc
       , MAX(CAST(chartdate AS DATE)) AS chartdate
       , MAX(charttime)               AS charttime
       , MAX(CASE WHEN org_name IS NOT NULL AND org_name != ''
                  THEN 1 ELSE 0 END)  AS positiveculture
  FROM   microbiologyevents
  GROUP  BY subject_id, hadm_id, chartdate, charttime,
           spec_itemid, spec_type_desc
)
, me_then_ab AS
(
  SELECT ab_tbl.subject_id
       , ab_tbl.hadm_id
       , ab_tbl.icustay_id
       , ab_tbl.ab_id
       , COALESCE(me72.charttime,
                  CAST(me72.chartdate AS TIMESTAMP)) AS last72_charttime
       , me72.positiveculture                       AS last72_positiveculture
       , me72.spec_type_desc                        AS last72_specimen
       , ROW_NUMBER() OVER (
           PARTITION BY ab_tbl.subject_id, ab_tbl.ab_id
           ORDER BY     me72.chartdate, me72.charttime NULLS LAST
         ) AS micro_seq
  FROM   ab_tbl
  LEFT   JOIN me me72
         ON  ab_tbl.subject_id = me72.subject_id
         AND (
              (
                me72.charttime IS NOT NULL
                AND ab_tbl.antibiotic_time >  me72.charttime
                AND ab_tbl.antibiotic_time <= DATETIME_ADD(me72.charttime, INTERVAL '72' HOUR)
              )
           OR (
                me72.charttime IS NULL
                AND ab_tbl.antibiotic_date >= me72.chartdate
                AND ab_tbl.antibiotic_date <= me72.chartdate + INTERVAL '3 day'
              )
         )
)
, ab_then_me AS
(
  SELECT ab_tbl.subject_id
       , ab_tbl.hadm_id
       , ab_tbl.icustay_id
       , ab_tbl.ab_id
       , COALESCE(me24.charttime,
                  CAST(me24.chartdate AS TIMESTAMP)) AS next24_charttime
       , me24.positiveculture                       AS next24_positiveculture
       , me24.spec_type_desc                        AS next24_specimen
       , ROW_NUMBER() OVER (
           PARTITION BY ab_tbl.subject_id, ab_tbl.ab_id
           ORDER BY     me24.chartdate, me24.charttime NULLS LAST
         ) AS micro_seq
  FROM   ab_tbl
  LEFT   JOIN me me24
         ON  ab_tbl.subject_id = me24.subject_id
         AND (
              (
                me24.charttime IS NOT NULL
                AND ab_tbl.antibiotic_time >= DATETIME_SUB(me24.charttime, INTERVAL '24' HOUR)
                AND ab_tbl.antibiotic_time <  me24.charttime
              )
           OR (
                me24.charttime IS NULL
                AND ab_tbl.antibiotic_date >= me24.chartdate - INTERVAL '1 day'
                AND ab_tbl.antibiotic_date <= me24.chartdate
              )
         )
)
SELECT ab_tbl.subject_id
     , ab_tbl.icustay_id
     , ab_tbl.hadm_id
     , ab_tbl.ab_id
     , ab_tbl.antibiotic
     , ab_tbl.antibiotic_time

     , CASE
         WHEN me2ab.last72_specimen IS NULL AND ab2me.next24_specimen IS NULL
         THEN 0 ELSE 1
       END AS suspected_infection

     , CASE
         WHEN me2ab.last72_specimen IS NULL AND ab2me.next24_specimen IS NULL
         THEN NULL
         ELSE COALESCE(me2ab.last72_charttime, ab_tbl.antibiotic_time)
       END AS suspected_infection_time

     , COALESCE(me2ab.last72_charttime, ab2me.next24_charttime) AS culture_time
     , COALESCE(me2ab.last72_specimen,  ab2me.next24_specimen)  AS specimen
     , COALESCE(me2ab.last72_positiveculture,
                ab2me.next24_positiveculture) AS positive_culture
FROM   ab_tbl
LEFT   JOIN ab_then_me ab2me
       ON  ab_tbl.subject_id = ab2me.subject_id
       AND ab_tbl.ab_id      = ab2me.ab_id
       AND ab2me.micro_seq   = 1
LEFT   JOIN me_then_ab me2ab
       ON  ab_tbl.subject_id = me2ab.subject_id
       AND ab_tbl.ab_id      = me2ab.ab_id
       AND me2ab.micro_seq   = 1;

CREATE INDEX IF NOT EXISTS suspicion_of_infection_idx
  ON suspicion_of_infection (icustay_id, suspected_infection_time);
