-- =====================================================
-- 运动传感设备时序数据表设计（PostgreSQL/TimescaleDB）
-- 表名前缀均为 ss_
-- 适用于高吞吐量、高并发的生命体征与位置数据存储
-- =====================================================

-- 1. 扩展与准备
-- -------------------------------------
-- 需要 uuid-ossp 与 timescaledb（若使用时序库）
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS timescaledb;  -- 启用 TimescaleDB 扩展

-- Enable PostGIS extension for spatial data
CREATE EXTENSION IF NOT EXISTS postgis;

-- 2. 核心设备关联表
-- -------------------------------------
-- 设备管理表引用 ak_devices 表
-- 传感器测量数据均关联到已有设备管理

-- 3. 基础时序数据表：通用测量记录
-- -------------------------------------
CREATE TABLE IF NOT EXISTS public.ss_sensor_measurements (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    device_id UUID NOT NULL REFERENCES public.ak_devices(id) ON DELETE CASCADE,
    user_id UUID REFERENCES public.ak_users(id) ON DELETE SET NULL,
    measurement_type TEXT NOT NULL,  -- 如 steps, heart_rate, spo2, stride, bp, temp, location, ecg
    measured_at TIMESTAMPTZ NOT NULL,
    unit TEXT,                       -- 单位，如 count, bpm, %, cm, mmHg, ℃, geojson
    raw_data JSONB,                  -- 原始数据或扩展字段
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- 高并发场景下用 TimescaleDB 创建 hypertable：
-- SELECT create_hypertable('public.ss_sensor_measurements', 'measured_at', chunk_time_interval => interval '1 day');

CREATE INDEX ON public.ss_sensor_measurements(device_id, measured_at DESC);
CREATE INDEX ON public.ss_sensor_measurements(measurement_type, measured_at DESC);

-- 4. 分专题数据表：性能优化（可选）
-- -------------------------------------
-- 对于最常见的生命体征，可划分专项表，以提高查询效率

-- 步数
CREATE TABLE IF NOT EXISTS public.ss_steps (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    measurement_id UUID NOT NULL REFERENCES public.ss_sensor_measurements(id) ON DELETE CASCADE,
    step_count BIGINT NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX ON public.ss_steps(step_count);

-- 心率
CREATE TABLE IF NOT EXISTS public.ss_heart_rate (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    measurement_id UUID NOT NULL REFERENCES public.ss_sensor_measurements(id) ON DELETE CASCADE,
    heart_rate_bpm INT NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX ON public.ss_heart_rate(heart_rate_bpm);

-- 血氧 (SpO2)
CREATE TABLE IF NOT EXISTS public.ss_spo2 (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    measurement_id UUID NOT NULL REFERENCES public.ss_sensor_measurements(id) ON DELETE CASCADE,
    spo2_percent INT NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX ON public.ss_spo2(spo2_percent);

-- 血压
CREATE TABLE IF NOT EXISTS public.ss_blood_pressure (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    measurement_id UUID NOT NULL REFERENCES public.ss_sensor_measurements(id) ON DELETE CASCADE,
    systolic INT NOT NULL,
    diastolic INT NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX ON public.ss_blood_pressure(systolic, diastolic);

-- 体温
CREATE TABLE IF NOT EXISTS public.ss_temperature (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    measurement_id UUID NOT NULL REFERENCES public.ss_sensor_measurements(id) ON DELETE CASCADE,
    temperature_c FLOAT NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX ON public.ss_temperature(temperature_c);

-- 步幅
CREATE TABLE IF NOT EXISTS public.ss_stride_length (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    measurement_id UUID NOT NULL REFERENCES public.ss_sensor_measurements(id) ON DELETE CASCADE,
    stride_cm FLOAT NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX ON public.ss_stride_length(stride_cm);

-- 位置（GeoJSON；使用经纬度替代 PostGIS 类型）
CREATE TABLE IF NOT EXISTS public.ss_location (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    measurement_id UUID NOT NULL REFERENCES public.ss_sensor_measurements(id) ON DELETE CASCADE,
    latitude DOUBLE PRECISION NOT NULL,  -- 纬度
    longitude DOUBLE PRECISION NOT NULL, -- 经度
    altitude FLOAT,
    speed FLOAT,
    heading FLOAT,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX ON public.ss_location(latitude, longitude);

-- 心电图 (ECG) 波形数据
-- 分两表：测量元信息 + 样本点
CREATE TABLE IF NOT EXISTS public.ss_ecg_measurements (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    measurement_id UUID NOT NULL REFERENCES public.ss_sensor_measurements(id) ON DELETE CASCADE,
    duration_sec FLOAT NOT NULL,
    sample_rate_hz FLOAT NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE IF NOT EXISTS public.ss_ecg_samples (
    ecg_id UUID NOT NULL REFERENCES public.ss_ecg_measurements(id) ON DELETE CASCADE,
    sample_index INT NOT NULL,
    voltage_mv FLOAT NOT NULL,
    PRIMARY KEY(ecg_id, sample_index)
);
CREATE INDEX ON public.ss_ecg_samples(sample_index);

-- 呼吸率
CREATE TABLE IF NOT EXISTS public.ss_respiratory_rate (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    measurement_id UUID NOT NULL REFERENCES public.ss_sensor_measurements(id) ON DELETE CASCADE,
    respiratory_rate INT NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX ON public.ss_respiratory_rate(respiratory_rate);

-- 潮气量
CREATE TABLE IF NOT EXISTS public.ss_tidal_volume (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    measurement_id UUID NOT NULL REFERENCES public.ss_sensor_measurements(id) ON DELETE CASCADE,
    tidal_volume_ml FLOAT NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX ON public.ss_tidal_volume(tidal_volume_ml);

-- 呼吸音
CREATE TABLE IF NOT EXISTS public.ss_respiratory_sounds (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    measurement_id UUID NOT NULL REFERENCES public.ss_sensor_measurements(id) ON DELETE CASCADE,
    audio_data BYTEA NOT NULL, -- 原始音频
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- 心率变异性
CREATE TABLE IF NOT EXISTS public.ss_hrv (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    measurement_id UUID NOT NULL REFERENCES public.ss_sensor_measurements(id) ON DELETE CASCADE,
    hrv_rmssd FLOAT,
    hrv_sdnn FLOAT,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX ON public.ss_hrv(hrv_rmssd);
CREATE INDEX ON public.ss_hrv(hrv_sdnn);

-- 血糖
CREATE TABLE IF NOT EXISTS public.ss_glucose (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    measurement_id UUID NOT NULL REFERENCES public.ss_sensor_measurements(id) ON DELETE CASCADE,
    glucose_mg_dl FLOAT NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX ON public.ss_glucose(glucose_mg_dl);

-- 皮肤电反应 (GSR)
CREATE TABLE IF NOT EXISTS public.ss_gsr (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    measurement_id UUID NOT NULL REFERENCES public.ss_sensor_measurements(id) ON DELETE CASCADE,
    gsr_us FLOAT NOT NULL, -- 微西门子
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX ON public.ss_gsr(gsr_us);

-- 皮肤温度
CREATE TABLE IF NOT EXISTS public.ss_skin_temperature (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    measurement_id UUID NOT NULL REFERENCES public.ss_sensor_measurements(id) ON DELETE CASCADE,
    skin_temp_c FLOAT NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX ON public.ss_skin_temperature(skin_temp_c);

-- 卡路里消耗
CREATE TABLE IF NOT EXISTS public.ss_energy_expenditure (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    measurement_id UUID NOT NULL REFERENCES public.ss_sensor_measurements(id) ON DELETE CASCADE,
    calories_kcal FLOAT NOT NULL,
    met FLOAT,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX ON public.ss_energy_expenditure(calories_kcal);

-- VO2 / VO2max
CREATE TABLE IF NOT EXISTS public.ss_vo2 (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    measurement_id UUID NOT NULL REFERENCES public.ss_sensor_measurements(id) ON DELETE CASCADE,
    vo2_ml_per_min FLOAT,
    vo2max_ml_per_min FLOAT,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX ON public.ss_vo2(vo2_ml_per_min);
CREATE INDEX ON public.ss_vo2(vo2max_ml_per_min);

-- 睡眠指标
CREATE TABLE IF NOT EXISTS public.ss_sleep_metrics (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    measurement_id UUID NOT NULL REFERENCES public.ss_sensor_measurements(id) ON DELETE CASCADE,
    sleep_stage VARCHAR(32) NOT NULL, -- awake, light, deep, rem
    duration_sec INT NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX ON public.ss_sleep_metrics(sleep_stage);

-- 跌倒事件
CREATE TABLE IF NOT EXISTS public.ss_fall_events (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    measurement_id UUID NOT NULL REFERENCES public.ss_sensor_measurements(id) ON DELETE CASCADE,
    fall_detected BOOLEAN NOT NULL,
    event_time TIMESTAMPTZ NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX ON public.ss_fall_events(fall_detected);

-- 姿态数据
CREATE TABLE IF NOT EXISTS public.ss_posture (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    measurement_id UUID NOT NULL REFERENCES public.ss_sensor_measurements(id) ON DELETE CASCADE,
    posture VARCHAR(32) NOT NULL, -- standing, sitting, lying, walking
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX ON public.ss_posture(posture);

-- 肌电 (EMG)
CREATE TABLE IF NOT EXISTS public.ss_emg_measurements (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    measurement_id UUID NOT NULL REFERENCES public.ss_sensor_measurements(id) ON DELETE CASCADE,
    duration_sec FLOAT NOT NULL,
    sample_rate_hz FLOAT NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE IF NOT EXISTS public.ss_emg_samples (
    emg_id UUID NOT NULL REFERENCES public.ss_emg_measurements(id) ON DELETE CASCADE,
    sample_index INT NOT NULL,
    voltage_mv FLOAT NOT NULL,
    PRIMARY KEY(emg_id, sample_index)
);
CREATE INDEX ON public.ss_emg_samples(sample_index);

-- 脑电 (EEG)
CREATE TABLE IF NOT EXISTS public.ss_eeg_measurements (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    measurement_id UUID NOT NULL REFERENCES public.ss_sensor_measurements(id) ON DELETE CASCADE,
    duration_sec FLOAT NOT NULL,
    sample_rate_hz FLOAT NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE IF NOT EXISTS public.ss_eeg_samples (
    eeg_id UUID NOT NULL REFERENCES public.ss_eeg_measurements(id) ON DELETE CASCADE,
    sample_index INT NOT NULL,
    voltage_uv FLOAT NOT NULL,
    PRIMARY KEY(eeg_id, sample_index)
);
CREATE INDEX ON public.ss_eeg_samples(sample_index);

-- 血乳酸
CREATE TABLE IF NOT EXISTS public.ss_lactate (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    measurement_id UUID NOT NULL REFERENCES public.ss_sensor_measurements(id) ON DELETE CASCADE,
    lactate_mmol_l FLOAT NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX ON public.ss_lactate(lactate_mmol_l);

-- PPG 波形
CREATE TABLE IF NOT EXISTS public.ss_ppg_measurements (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    measurement_id UUID NOT NULL REFERENCES public.ss_sensor_measurements(id) ON DELETE CASCADE,
    duration_sec FLOAT NOT NULL,
    sample_rate_hz FLOAT NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE IF NOT EXISTS public.ss_ppg_samples (
    ppg_id UUID NOT NULL REFERENCES public.ss_ppg_measurements(id) ON DELETE CASCADE,
    sample_index INT NOT NULL,
    amplitude FLOAT NOT NULL,
    PRIMARY KEY(ppg_id, sample_index)
);
CREATE INDEX ON public.ss_ppg_samples(sample_index);

-- 5. 数据保留与归档（示例）
-- -------------------------------------
-- 定期归档或删除旧数据，可使用 TimescaleDB retention policy
-- SELECT add_retention_policy('public.ss_sensor_measurements', INTERVAL '90 days');

-- 6. 视图和物化视图示例
-- -------------------------------------
-- 最近 1 小时心率聚合
CREATE MATERIALIZED VIEW public.ss_hr_last_hour_summary AS
SELECT device_id,
       minute_bucket,
       avg(heart_rate_bpm) AS avg_bpm,
       min(heart_rate_bpm) AS min_bpm,
       max(heart_rate_bpm) AS max_bpm
FROM (
  SELECT date_trunc('minute', sm.measured_at) AS minute_bucket,
         hr.heart_rate_bpm,
         sm.device_id
  FROM public.ss_sensor_measurements sm
  JOIN public.ss_heart_rate hr ON hr.measurement_id = sm.id
  WHERE sm.measured_at > now() - INTERVAL '1 hour'
) sub
GROUP BY device_id, minute_bucket;

-- =====================================================
-- 使用说明：
-- • 通用写入：将任意指标写入 ss_sensor_measurements，随后写入专项表
-- • 查询优化：针对常用指标查询专项表
-- • 时序特性：开启 TimescaleDB hypertable 以提高写入与查询性能
-- =====================================================
