begin;

-- roles
alter schema public owner to sensor_admin;
grant usage on schema public to sensor_writer;

grant all on all tables in schema public to sensor_admin;
grant all on all sequences in schema public to sensor_admin;

grant insert, update, select on all tables in schema public to sensor_writer;
grant usage, select on all sequences in schema public to sensor_writer;

alter default privileges for role sensor_admin in schema public
    grant insert, update, select on tables to sensor_writer;

alter default privileges for role sensor_admin in schema public
    grant usage, select on sequences to sensor_writer;

-- pg_partman
create schema if not exists partman;
create extension if not exists pg_partman schema partman;

do $$
begin
  if not exists (select 1 from pg_roles where rolname = 'partman_user') then
    create role partman_user with login;
  end if;
end$$;

grant all on schema partman to partman_user;
grant all on all tables in schema partman to partman_user;
grant execute on all functions in schema partman to partman_user;
grant execute on all procedures in schema partman to partman_user;

grant all on schema public to partman_user;
grant all on all tables in schema public to partman_user;
grant temporary on database sensor_db to partman_user;
grant create on database sensor_db to partman_user;

grant usage, create on schema partman to sensor_admin;
grant execute on all functions in schema partman to sensor_admin;
grant all on all tables in schema partman to sensor_admin;
grant all on all sequences in schema partman to sensor_admin;

set role sensor_admin;

-- physical/transport endpoints
create table if not exists endpoints (
    endpoint_id int primary key generated by default as identity,
    endpoint_key text not null unique,

    -- 'snmp' | 'mqtt' | 'visa'
    protocol text not null,
    conn jsonb not null default '{}'::jsonb,

    is_enabled boolean not null default true,

    created_at timestamptz not null default now(),
    updated_at timestamptz not null default now()
);

create index if not exists idx_endpoints_enabled_protocol
on endpoints (is_enabled, protocol);

create table if not exists locations (
    location_id int primary key generated by default as identity,
    name text not null,
    is_active bool not null default true,
    constraint locations_name_unique unique (name)
);

create table if not exists devices (
    device_id int primary key generated by default as identity,
    -- "snmp:10.0.0.12:probe1"
    -- "mqtt:esp32-1"
    -- "visa:prologix:gpib5"
    device_key text not null unique,

    endpoint_id int references endpoints(endpoint_id),
    location_id int references locations(location_id),
    is_enabled boolean not null default true,

    metadata jsonb not null default '{}'::jsonb,

    created_at timestamptz not null default now(),
    updated_at timestamptz not null default now()
);

create index if not exists idx_devices_endpoint on devices (endpoint_id);
create index if not exists idx_devices_location on devices (location_id);
create index if not exists idx_devices_enabled on devices (is_enabled);

-- per-device measurement defs
create table if not exists device_channels (
    channel_id int primary key generated by default as identity,
    device_id int not null references devices(device_id) on delete cascade,

    -- corresponds to a column name in sensor_data (wide mapping)
    metric text not null,

    -- SNMP: {"type":"snmp_oid","oid":".1.3.6...","datatype":"float"}
    -- MQTT: {"type":"mqtt_topic","topic":"sensors/x/telemetry","payload":"json","field":"temperature_c"}
    -- VISA: {"type":"scpi","query":"MEAS:VOLT:DC?","read_termination":"\n"}
    source jsonb not null,

    scale_value double precision not null default 1.0,
    offset_value double precision not null default 0.0,

    poll_interval_s int,
    is_enabled boolean not null default true,

    created_at timestamptz not null default now(),

    constraint device_channels_unique_metric unique (device_id, metric)
);

create index if not exists idx_device_channels_device on device_channels (device_id);

-- device health/status
create table if not exists device_status (
    device_id int primary key references devices(device_id) on delete cascade,
    last_seen timestamptz,
    last_ok timestamptz,
    last_error_at timestamptz,
    last_error text,
    updated_at timestamptz not null default now()
);

create table if not exists sensor_data (
    ts          timestamptz not null,
    device_id   int not null references devices(device_id),
    location_id int references locations(location_id),

    temp_c      real,
    humidity_rh real,
    pressure_pa real,
    light_lux   real,
    soil_moist  real,
    co2_ppm     real,

    voltage_v   real,
    current_a   real,
    resistance_ohm real,
    freq_hz     real,
    power_w     real,

    constraint sensor_data_pk primary key (device_id, ts)
) partition by range (ts);

create index if not exists idx_sensor_data_device_ts on sensor_data (device_id, ts desc);
create index if not exists idx_sensor_data_location_ts on sensor_data (location_id, ts desc);
create index if not exists idx_sensor_data_ts_brin on sensor_data using brin(ts);

-- partition
select partman.create_parent(
    p_parent_table  => 'public.sensor_data',
    p_control       => 'ts',
    p_interval      => '1 year',
    p_premake       => 2
);

commit;

