CREATE EXTENSION IF NOT EXISTS timescaledb;

CREATE TABLE IF NOT EXISTS edge (
  id text PRIMARY KEY,
  name text NOT NULL,
  parent_id text REFERENCES edge(id) ON DELETE RESTRICT,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE TABLE IF NOT EXISTS tag (
  id text PRIMARY KEY,
  name text NOT NULL,
  tag_group text,
  min_value double precision,
  max_value double precision,
  unit_of_measurement text,
  comment text,
  precision smallint,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE TABLE IF NOT EXISTS edge_tag (
  edge_id text NOT NULL REFERENCES edge(id) ON DELETE CASCADE,
  tag_id text NOT NULL REFERENCES tag(id) ON DELETE CASCADE,
  created_at timestamptz NOT NULL DEFAULT now(),
  PRIMARY KEY (edge_id, tag_id)
);

CREATE TABLE IF NOT EXISTS history_points (
  time timestamptz NOT NULL,
  edge_id text NOT NULL,
  tag_id text NOT NULL,
  value double precision NOT NULL,
  received_at timestamptz NOT NULL DEFAULT now()
);

SELECT create_hypertable(
  'history_points',
  'time',
  chunk_time_interval => INTERVAL '1 day',
  if_not_exists => TRUE
);

CREATE INDEX IF NOT EXISTS history_points_edge_tag_time_desc_idx
  ON history_points (edge_id, tag_id, time DESC)
  INCLUDE (value);

CREATE INDEX IF NOT EXISTS history_points_time_brin_idx
  ON history_points USING brin (time);

CREATE TABLE IF NOT EXISTS current_values (
  edge_id text NOT NULL,
  tag_id text NOT NULL,
  value double precision NOT NULL,
  time timestamptz NOT NULL,
  updated_at timestamptz NOT NULL DEFAULT now(),
  PRIMARY KEY (edge_id, tag_id)
);

CREATE INDEX IF NOT EXISTS current_values_updated_at_idx
  ON current_values (updated_at DESC);

ALTER TABLE history_points SET (
  timescaledb.compress,
  timescaledb.compress_segmentby = 'edge_id,tag_id',
  timescaledb.compress_orderby = 'time DESC'
);

SELECT add_compression_policy(
  'history_points',
  INTERVAL '7 days',
  if_not_exists => TRUE
);

CREATE MATERIALIZED VIEW IF NOT EXISTS history_1m
WITH (timescaledb.continuous) AS
SELECT
  time_bucket(INTERVAL '1 minute', time) AS bucket,
  edge_id,
  tag_id,
  first(value, time) AS first_value,
  min(value) AS min_value,
  max(value) AS max_value,
  avg(value) AS avg_value,
  last(value, time) AS last_value,
  count(*)::integer AS point_count
FROM history_points
GROUP BY bucket, edge_id, tag_id
WITH NO DATA;

CREATE MATERIALIZED VIEW IF NOT EXISTS history_5m
WITH (timescaledb.continuous) AS
SELECT
  time_bucket(INTERVAL '5 minutes', time) AS bucket,
  edge_id,
  tag_id,
  first(value, time) AS first_value,
  min(value) AS min_value,
  max(value) AS max_value,
  avg(value) AS avg_value,
  last(value, time) AS last_value,
  count(*)::integer AS point_count
FROM history_points
GROUP BY bucket, edge_id, tag_id
WITH NO DATA;

CREATE MATERIALIZED VIEW IF NOT EXISTS history_1h
WITH (timescaledb.continuous) AS
SELECT
  time_bucket(INTERVAL '1 hour', time) AS bucket,
  edge_id,
  tag_id,
  first(value, time) AS first_value,
  min(value) AS min_value,
  max(value) AS max_value,
  avg(value) AS avg_value,
  last(value, time) AS last_value,
  count(*)::integer AS point_count
FROM history_points
GROUP BY bucket, edge_id, tag_id
WITH NO DATA;

CREATE MATERIALIZED VIEW IF NOT EXISTS history_1d
WITH (timescaledb.continuous) AS
SELECT
  time_bucket(INTERVAL '1 day', time) AS bucket,
  edge_id,
  tag_id,
  first(value, time) AS first_value,
  min(value) AS min_value,
  max(value) AS max_value,
  avg(value) AS avg_value,
  last(value, time) AS last_value,
  count(*)::integer AS point_count
FROM history_points
GROUP BY bucket, edge_id, tag_id
WITH NO DATA;

CREATE INDEX IF NOT EXISTS history_1m_edge_tag_bucket_idx
  ON history_1m (edge_id, tag_id, bucket DESC);

CREATE INDEX IF NOT EXISTS history_5m_edge_tag_bucket_idx
  ON history_5m (edge_id, tag_id, bucket DESC);

CREATE INDEX IF NOT EXISTS history_1h_edge_tag_bucket_idx
  ON history_1h (edge_id, tag_id, bucket DESC);

CREATE INDEX IF NOT EXISTS history_1d_edge_tag_bucket_idx
  ON history_1d (edge_id, tag_id, bucket DESC);

DO $$
BEGIN
  PERFORM add_continuous_aggregate_policy(
    'history_1m',
    start_offset => INTERVAL '7 days',
    end_offset => INTERVAL '1 minute',
    schedule_interval => INTERVAL '1 minute'
  );
EXCEPTION
  WHEN duplicate_object THEN NULL;
END $$;

DO $$
BEGIN
  PERFORM add_continuous_aggregate_policy(
    'history_5m',
    start_offset => INTERVAL '30 days',
    end_offset => INTERVAL '5 minutes',
    schedule_interval => INTERVAL '5 minutes'
  );
EXCEPTION
  WHEN duplicate_object THEN NULL;
END $$;

DO $$
BEGIN
  PERFORM add_continuous_aggregate_policy(
    'history_1h',
    start_offset => INTERVAL '180 days',
    end_offset => INTERVAL '1 hour',
    schedule_interval => INTERVAL '15 minutes'
  );
EXCEPTION
  WHEN duplicate_object THEN NULL;
END $$;

DO $$
BEGIN
  PERFORM add_continuous_aggregate_policy(
    'history_1d',
    start_offset => INTERVAL '5 years',
    end_offset => INTERVAL '1 day',
    schedule_interval => INTERVAL '1 hour'
  );
EXCEPTION
  WHEN duplicate_object THEN NULL;
END $$;
