-- TimescaleDB initialization script
-- Creates hypertables for time-series metrics storage

-- Enable TimescaleDB extension
CREATE EXTENSION IF NOT EXISTS timescaledb;

-- Raw metrics table (high resolution, short retention)
CREATE TABLE IF NOT EXISTS metrics_raw (
    time        TIMESTAMPTZ NOT NULL,
    machine_id  TEXT NOT NULL,
    hostname    TEXT NOT NULL,
    metric_type TEXT NOT NULL,
    value       DOUBLE PRECISION NOT NULL,
    labels      JSONB DEFAULT '{}'::jsonb
);

-- Convert to hypertable with 1-hour chunks
SELECT create_hypertable('metrics_raw', 'time',
    chunk_time_interval => INTERVAL '1 hour',
    if_not_exists => TRUE
);

-- Create indexes for common queries
CREATE INDEX IF NOT EXISTS idx_metrics_raw_machine
    ON metrics_raw (machine_id, time DESC);
CREATE INDEX IF NOT EXISTS idx_metrics_raw_type
    ON metrics_raw (metric_type, time DESC);

-- Aggregated metrics table (1-minute resolution, longer retention)
CREATE TABLE IF NOT EXISTS metrics_1m (
    time        TIMESTAMPTZ NOT NULL,
    machine_id  TEXT NOT NULL,
    hostname    TEXT NOT NULL,
    metric_type TEXT NOT NULL,
    avg_value   DOUBLE PRECISION NOT NULL,
    min_value   DOUBLE PRECISION NOT NULL,
    max_value   DOUBLE PRECISION NOT NULL,
    sample_count INTEGER NOT NULL
);

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

CREATE INDEX IF NOT EXISTS idx_metrics_1m_machine
    ON metrics_1m (machine_id, time DESC);

-- Aggregated metrics table (1-hour resolution, long retention)
CREATE TABLE IF NOT EXISTS metrics_1h (
    time        TIMESTAMPTZ NOT NULL,
    machine_id  TEXT NOT NULL,
    hostname    TEXT NOT NULL,
    metric_type TEXT NOT NULL,
    avg_value   DOUBLE PRECISION NOT NULL,
    min_value   DOUBLE PRECISION NOT NULL,
    max_value   DOUBLE PRECISION NOT NULL,
    sample_count INTEGER NOT NULL
);

SELECT create_hypertable('metrics_1h', 'time',
    chunk_time_interval => INTERVAL '1 week',
    if_not_exists => TRUE
);

CREATE INDEX IF NOT EXISTS idx_metrics_1h_machine
    ON metrics_1h (machine_id, time DESC);

-- Machines registry
CREATE TABLE IF NOT EXISTS machines (
    machine_id  TEXT PRIMARY KEY,
    hostname    TEXT NOT NULL,
    first_seen  TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    last_seen   TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    metadata    JSONB DEFAULT '{}'::jsonb,
    health      TEXT NOT NULL DEFAULT 'UNKNOWN'
);

-- Alert rules configuration
CREATE TABLE IF NOT EXISTS alert_rules (
    id          SERIAL PRIMARY KEY,
    name        TEXT NOT NULL UNIQUE,
    metric_type TEXT NOT NULL,
    operator    TEXT NOT NULL CHECK (operator IN ('gt', 'lt', 'gte', 'lte', 'eq')),
    threshold   DOUBLE PRECISION NOT NULL,
    severity    TEXT NOT NULL CHECK (severity IN ('warning', 'critical')),
    enabled     BOOLEAN NOT NULL DEFAULT TRUE,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- Alert history
CREATE TABLE IF NOT EXISTS alerts (
    id          SERIAL,
    time        TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    machine_id  TEXT NOT NULL,
    rule_id     INTEGER REFERENCES alert_rules(id),
    rule_name   TEXT NOT NULL,
    metric_type TEXT NOT NULL,
    value       DOUBLE PRECISION NOT NULL,
    threshold   DOUBLE PRECISION NOT NULL,
    severity    TEXT NOT NULL,
    resolved_at TIMESTAMPTZ,
    PRIMARY KEY (id, time)
);

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

-- Retention policies
-- Raw data: 24 hours
SELECT add_retention_policy('metrics_raw', INTERVAL '24 hours', if_not_exists => TRUE);

-- 1-minute aggregates: 7 days
SELECT add_retention_policy('metrics_1m', INTERVAL '7 days', if_not_exists => TRUE);

-- 1-hour aggregates: 90 days
SELECT add_retention_policy('metrics_1h', INTERVAL '90 days', if_not_exists => TRUE);

-- Alerts: 30 days
SELECT add_retention_policy('alerts', INTERVAL '30 days', if_not_exists => TRUE);

-- Continuous aggregates for automatic downsampling
CREATE MATERIALIZED VIEW IF NOT EXISTS metrics_1m_agg
WITH (timescaledb.continuous) AS
SELECT
    time_bucket('1 minute', time) AS time,
    machine_id,
    hostname,
    metric_type,
    AVG(value) AS avg_value,
    MIN(value) AS min_value,
    MAX(value) AS max_value,
    COUNT(*) AS sample_count
FROM metrics_raw
GROUP BY time_bucket('1 minute', time), machine_id, hostname, metric_type
WITH NO DATA;

-- Refresh policy for continuous aggregate
SELECT add_continuous_aggregate_policy('metrics_1m_agg',
    start_offset => INTERVAL '1 hour',
    end_offset => INTERVAL '1 minute',
    schedule_interval => INTERVAL '1 minute',
    if_not_exists => TRUE
);

-- Insert default alert rules
INSERT INTO alert_rules (name, metric_type, operator, threshold, severity)
VALUES
    ('High CPU Usage', 'CPU_PERCENT', 'gt', 80.0, 'warning'),
    ('Critical CPU Usage', 'CPU_PERCENT', 'gt', 95.0, 'critical'),
    ('High Memory Usage', 'MEMORY_PERCENT', 'gt', 85.0, 'warning'),
    ('Critical Memory Usage', 'MEMORY_PERCENT', 'gt', 95.0, 'critical'),
    ('High Disk Usage', 'DISK_PERCENT', 'gt', 80.0, 'warning'),
    ('Critical Disk Usage', 'DISK_PERCENT', 'gt', 90.0, 'critical')
ON CONFLICT (name) DO NOTHING;
