-- Active: 1772601349048@@metro.proxy.rlwy.net@13239@railway
-- Athena provisioning script.
--
-- Applies all Athena schema tables to a fresh (or existing) Postgres database.
-- Every statement uses CREATE TABLE IF NOT EXISTS / CREATE INDEX IF NOT EXISTS
-- so the script is safe to re-run against an already-provisioned database.
--
-- Usage:
--   psql "$DATABASE_URL" -f sql/provision.sql
--   athena_rs provision --uri "$DATABASE_URL"

-- ---------------------------------------------------------------------------
-- Gateway request / operation logs
-- ---------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS gateway_request_log (
    id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
    created_at timestamptz NOT NULL DEFAULT now(),
    request_id text NOT NULL,
    client text,
    method text,
    path text,
    query_string text,
    status_code integer,
    ipv4 text,
    user_agent text,
    headers jsonb,
    body jsonb,
    user_id text,
    company_id text,
    organization_id text,
    api_key_id text,
    presented_api_key_public_id text,
    presented_api_key_hash text,
    presented_api_key_salt text,
    api_key_authenticated boolean DEFAULT false,
    api_key_authorized boolean DEFAULT false,
    api_key_enforced boolean DEFAULT false,
    api_key_auth_reason text,
    host text,
    cached boolean,
    duration_ms bigint,
    cache_lookup_outcome text,
    cache_source text,
    operation text,
    table_name text,
    time numeric NOT NULL,
    PRIMARY KEY (id)
);

CREATE INDEX IF NOT EXISTS idx_gateway_request_log_request_id
    ON gateway_request_log (request_id);

CREATE INDEX IF NOT EXISTS idx_gateway_request_log_created_at
    ON gateway_request_log (created_at DESC);

CREATE INDEX IF NOT EXISTS idx_gateway_request_log_client_created_at
    ON gateway_request_log (client, created_at DESC)
    WHERE client IS NOT NULL;

CREATE TABLE IF NOT EXISTS gateway_connection_registry_realtime (
    id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
    created_at timestamptz NOT NULL DEFAULT now(),
    registry_key text NOT NULL,
    client_name text NOT NULL,
    ipv4 text NOT NULL,
    athena_url text NOT NULL,
    api_key_public_id text,
    api_key_external_id text,
    api_key_internal_id bigint,
    api_key_authenticated boolean NOT NULL DEFAULT false,
    api_key_authorized boolean NOT NULL DEFAULT false,
    last_method text,
    last_path text,
    user_agent text,
    last_request_id text,
    last_status_code integer,
    total_hits bigint NOT NULL DEFAULT 1,
    first_seen_at timestamptz NOT NULL DEFAULT now(),
    last_seen_at timestamptz NOT NULL DEFAULT now(),
    updated_at timestamptz NOT NULL DEFAULT now(),
    PRIMARY KEY (id),
    CONSTRAINT gateway_connection_registry_realtime_registry_key_unique UNIQUE (registry_key)
);

CREATE INDEX IF NOT EXISTS idx_gateway_connection_registry_realtime_client
    ON gateway_connection_registry_realtime (client_name);

CREATE INDEX IF NOT EXISTS idx_gateway_connection_registry_realtime_last_seen
    ON gateway_connection_registry_realtime (last_seen_at DESC);

CREATE INDEX IF NOT EXISTS idx_gateway_connection_registry_realtime_ip
    ON gateway_connection_registry_realtime (ipv4);

CREATE INDEX IF NOT EXISTS idx_gateway_connection_registry_realtime_url
    ON gateway_connection_registry_realtime (athena_url);

CREATE TABLE IF NOT EXISTS gateway_operation_log (
    id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
    created_at timestamptz NOT NULL DEFAULT now(),
    request_id text NOT NULL,
    operation text,
    table_name text,
    client text,
    method text,
    path text,
    status_code integer,
    duration_ms bigint,
    details jsonb,
    time numeric NOT NULL,
    error boolean DEFAULT false,
    message text,
    cache_key text,
    cache_lookup_outcome text,
    cache_source text,
    cached boolean,
    PRIMARY KEY (id)
);

-- ---------------------------------------------------------------------------
-- Pipeline run step log (POST /pipelines); written to logging DB when configured
-- ---------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS pipeline_step_log (
    id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    created_at timestamptz NOT NULL DEFAULT now(),
    pipeline_run_id uuid NOT NULL,
    request_id text,
    pipeline_name text,
    step text NOT NULL,
    athena_client text,
    table_name text,
    status_code integer,
    duration_ms bigint,
    error_message text,
    details jsonb,
    CONSTRAINT pipeline_step_log_step_check CHECK (step IN ('source', 'transform', 'sink'))
);

CREATE INDEX IF NOT EXISTS idx_pipeline_step_log_pipeline_run_id
    ON pipeline_step_log (pipeline_run_id);

CREATE INDEX IF NOT EXISTS idx_pipeline_step_log_created_at
    ON pipeline_step_log (created_at DESC);

CREATE INDEX IF NOT EXISTS idx_pipeline_step_log_pipeline_name_created_at
    ON pipeline_step_log (pipeline_name, created_at DESC)
    WHERE pipeline_name IS NOT NULL;

CREATE EXTENSION IF NOT EXISTS pgcrypto;

CREATE TABLE IF NOT EXISTS public.database_audit_log (
    id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    created_at timestamptz NOT NULL DEFAULT now(),
    database_audit_log_id uuid NOT NULL DEFAULT gen_random_uuid() UNIQUE,
    time bigint NOT NULL DEFAULT EXTRACT(EPOCH FROM now())::bigint,
    request_id uuid NOT NULL,
    client_name text NOT NULL,
    schema_name text NOT NULL,
    table_name text,
    object_type text NOT NULL,
    operation text NOT NULL,
    status text NOT NULL,
    api_key_id uuid,
    api_key_public_id text,
    user_id text,
    company_id text,
    organization_id text,
    request_payload jsonb NOT NULL DEFAULT '{}'::jsonb,
    executed_sql jsonb NOT NULL DEFAULT '[]'::jsonb,
    result_payload jsonb NOT NULL DEFAULT '{}'::jsonb,
    error_message text,
    remote_addr text,
    user_agent text,
    duration_ms bigint
);

CREATE INDEX IF NOT EXISTS database_audit_log_request_id_idx
    ON public.database_audit_log (request_id);

CREATE INDEX IF NOT EXISTS database_audit_log_client_created_at_idx
    ON public.database_audit_log (client_name, created_at DESC);

CREATE INDEX IF NOT EXISTS database_audit_log_schema_table_created_at_idx
    ON public.database_audit_log (schema_name, table_name, created_at DESC);

CREATE TABLE IF NOT EXISTS function_ddl_audit_log (
    id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    created_at timestamptz NOT NULL DEFAULT now(),
    function_ddl_audit_log_id uuid NOT NULL DEFAULT gen_random_uuid() UNIQUE,
    request_id uuid NOT NULL,
    client_name text NOT NULL,
    schema_name text NOT NULL,
    function_name text NOT NULL,
    function_signature text,
    action text NOT NULL,
    status text NOT NULL,
    ddl_sql text NOT NULL,
    previous_definition jsonb NOT NULL DEFAULT '[]'::jsonb,
    next_definition jsonb NOT NULL DEFAULT '[]'::jsonb,
    error_message text,
    api_key_id uuid,
    api_key_public_id text,
    user_id text,
    company_id text,
    organization_id text,
    remote_addr text,
    user_agent text,
    duration_ms bigint NOT NULL DEFAULT 0,
    time bigint NOT NULL DEFAULT EXTRACT(EPOCH FROM now())::bigint
);

CREATE INDEX IF NOT EXISTS idx_function_ddl_audit_log_request_id
    ON function_ddl_audit_log (request_id);

CREATE INDEX IF NOT EXISTS idx_function_ddl_audit_log_client_created_at
    ON function_ddl_audit_log (client_name, created_at DESC);

CREATE INDEX IF NOT EXISTS idx_function_ddl_audit_log_function
    ON function_ddl_audit_log (schema_name, function_name, created_at DESC);

CREATE TABLE IF NOT EXISTS gateway_deferred_request_queue (
    request_id text PRIMARY KEY,
    method text NOT NULL,
    route text NOT NULL,
    client_name text,
    request_bytes bigint,
    payload jsonb NOT NULL,
    status text NOT NULL DEFAULT 'queued',
    deferred_reason text,
    attempts integer NOT NULL DEFAULT 0,
    created_at timestamptz NOT NULL DEFAULT now(),
    updated_at timestamptz NOT NULL DEFAULT now(),
    completed_at timestamptz,
    error_message text,
    result_summary jsonb
);

CREATE INDEX IF NOT EXISTS idx_gateway_deferred_request_queue_status
    ON gateway_deferred_request_queue(status);

CREATE INDEX IF NOT EXISTS idx_gateway_deferred_request_queue_created_at
    ON gateway_deferred_request_queue(created_at DESC);

CREATE INDEX IF NOT EXISTS idx_gateway_deferred_request_queue_status_created
    ON gateway_deferred_request_queue(status, created_at ASC);

CREATE TABLE IF NOT EXISTS gateway_admission_events (
    id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    created_at timestamptz NOT NULL DEFAULT now(),
    event_id text NOT NULL,
    method text NOT NULL,
    route text NOT NULL,
    client_name text,
    request_bytes bigint,
    defer_requested boolean NOT NULL DEFAULT false,
    is_query_route boolean NOT NULL DEFAULT false,
    decision text NOT NULL,
    reason text,
    deferred_request_id text,
    retry_after_seconds integer,
    window_seconds integer,
    global_limit_per_window bigint,
    per_client_limit_per_window bigint,
    meta jsonb NOT NULL DEFAULT '{}'::jsonb
);

CREATE UNIQUE INDEX IF NOT EXISTS idx_gateway_admission_events_event_id
    ON gateway_admission_events(event_id);

CREATE INDEX IF NOT EXISTS idx_gateway_admission_events_created_at
    ON gateway_admission_events(created_at DESC);

CREATE INDEX IF NOT EXISTS idx_gateway_admission_events_decision
    ON gateway_admission_events(decision);

CREATE INDEX IF NOT EXISTS idx_gateway_admission_events_client_name
    ON gateway_admission_events(client_name);

-- ---------------------------------------------------------------------------
-- API keys and rights
-- ---------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS api_keys (
    id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    created_at timestamptz NOT NULL DEFAULT now(),
    api_keys_id uuid NOT NULL DEFAULT gen_random_uuid() UNIQUE,
    public_id text NOT NULL UNIQUE,
    name text NOT NULL,
    description text,
    client_name text,
    key_salt text NOT NULL,
    key_hash text NOT NULL,
    expires_at timestamptz,
    is_active boolean NOT NULL DEFAULT true,
    last_used_at timestamptz,
    updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_api_keys_client_name ON api_keys(client_name);
CREATE INDEX IF NOT EXISTS idx_api_keys_expires_at ON api_keys(expires_at);

CREATE TABLE IF NOT EXISTS api_key_rights (
    id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    api_key_rights_id uuid NOT NULL DEFAULT gen_random_uuid() UNIQUE,
    created_at timestamptz NOT NULL DEFAULT now(),
    name text NOT NULL UNIQUE,
    description text,
    updated_at timestamptz NOT NULL DEFAULT now()
);

-- Global rights catalog (assignable to any API key). Keeps Web Explorer / admin UI usable
-- on fresh databases without running separate migration scripts.
INSERT INTO api_key_rights (api_key_rights_id, name, description)
VALUES
    (gen_random_uuid(), 'gateway.access', 'General gateway access when enforcement uses the default fallback'),
    (gen_random_uuid(), 'gateway.read', 'Read rows via gateway fetch'),
    (gen_random_uuid(), 'gateway.write', 'Insert or update via gateway'),
    (gen_random_uuid(), 'gateway.delete', 'Delete rows via gateway'),
    (gen_random_uuid(), 'gateway.query', 'Execute SQL/CQL via gateway query'),
    (gen_random_uuid(), 'gateway.rpc.execute', 'Execute Postgres functions through Athena RPC routes'),
    (gen_random_uuid(), 'gateway.*', 'Wildcard gateway access'),
    (gen_random_uuid(), 'management.read', 'Read management capabilities for a client'),
    (gen_random_uuid(), 'management.tables.write', 'Create or alter managed tables'),
    (gen_random_uuid(), 'management.tables.drop', 'Drop managed tables'),
    (gen_random_uuid(), 'management.columns.write', 'Alter managed columns'),
    (gen_random_uuid(), 'management.columns.drop', 'Drop managed columns'),
    (gen_random_uuid(), 'management.indexes.write', 'Create managed indexes'),
    (gen_random_uuid(), 'management.indexes.drop', 'Drop managed indexes'),
    (gen_random_uuid(), 'management.functions.read', 'List managed Postgres functions'),
    (gen_random_uuid(), 'management.functions.write', 'Create or replace managed Postgres functions'),
    (gen_random_uuid(), 'management.functions.drop', 'Drop managed Postgres functions'),
    (gen_random_uuid(), 'management.*', 'Wildcard management access for a client')
ON CONFLICT (name) DO UPDATE
SET description = EXCLUDED.description,
    updated_at = now();

CREATE TABLE IF NOT EXISTS api_key_right_grants (
    id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
    api_key_right_grants_id uuid NOT NULL DEFAULT gen_random_uuid() UNIQUE,
    api_key_id bigint NOT NULL REFERENCES api_keys(id) ON DELETE CASCADE,
    right_id bigint NOT NULL REFERENCES api_key_rights(id) ON DELETE CASCADE,
    created_at timestamptz NOT NULL DEFAULT now(),
    PRIMARY KEY (api_key_id, right_id)
);

CREATE TABLE IF NOT EXISTS api_key_config (
    id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    api_key_config_id uuid NOT NULL DEFAULT gen_random_uuid() UNIQUE,
    enforce_api_keys boolean NOT NULL DEFAULT false,
    created_at timestamptz NOT NULL DEFAULT now(),
    updated_at timestamptz NOT NULL DEFAULT now()
);

INSERT INTO api_key_config (api_key_config_id, enforce_api_keys)
VALUES ('00000000-0000-0000-0000-000000000000', false)
ON CONFLICT (api_key_config_id) DO NOTHING;

CREATE TABLE IF NOT EXISTS api_key_client_config (
    id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    api_key_client_config_id uuid NOT NULL DEFAULT gen_random_uuid() UNIQUE,
    client_name text NOT NULL UNIQUE,
    enforce_api_keys boolean NOT NULL DEFAULT false,
    created_at timestamptz NOT NULL DEFAULT now(),
    updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE TABLE IF NOT EXISTS api_key_auth_log (
    id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    api_key_auth_log_id uuid NOT NULL DEFAULT gen_random_uuid() UNIQUE,
    created_at timestamptz NOT NULL DEFAULT now(),
    request_id text NOT NULL,
    api_key_id bigint REFERENCES api_keys(id) ON DELETE SET NULL,
    api_key_public_id text,
    client_name text,
    method text NOT NULL,
    path text NOT NULL,
    presented_key_hash text,
    presented_key_salt text,
    required_rights jsonb,
    granted_rights jsonb,
    authenticated boolean NOT NULL DEFAULT false,
    authorized boolean NOT NULL DEFAULT false,
    enforced boolean NOT NULL DEFAULT false,
    failure_reason text,
    remote_addr text,
    user_agent text,
    time timestamptz NOT NULL DEFAULT now(),
    UNIQUE(request_id, api_key_public_id)
);

CREATE INDEX IF NOT EXISTS idx_api_key_auth_log_api_key_id ON api_key_auth_log(api_key_id);
CREATE INDEX IF NOT EXISTS idx_api_key_auth_log_client_name ON api_key_auth_log(client_name);
CREATE INDEX IF NOT EXISTS idx_api_key_auth_log_time ON api_key_auth_log(time);

-- ---------------------------------------------------------------------------
-- Athena clients catalog
-- ---------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS athena_clients (
    id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
    created_at timestamptz NOT NULL DEFAULT now(),
    athena_client_id uuid NOT NULL DEFAULT gen_random_uuid() UNIQUE,
    client_name text NOT NULL,
    description text,
    pg_uri text,
    pg_uri_env_var text,
    config_uri_template text,
    source text NOT NULL DEFAULT 'database',
    is_active boolean NOT NULL DEFAULT true,
    is_frozen boolean NOT NULL DEFAULT false,
    last_synced_from_config_at timestamptz,
    last_seen_at timestamptz,
    metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
    updated_at timestamptz NOT NULL DEFAULT now(),
    deleted_at timestamptz,
    CONSTRAINT athena_clients_source_check CHECK (
        source IN ('config', 'database', 'merged')
    ),
    CONSTRAINT athena_clients_connection_check CHECK (
        pg_uri IS NOT NULL
        OR pg_uri_env_var IS NOT NULL
        OR config_uri_template IS NOT NULL
    )
);

CREATE UNIQUE INDEX IF NOT EXISTS idx_athena_clients_client_name_active
    ON athena_clients (lower(client_name))
    WHERE deleted_at IS NULL;


CREATE INDEX IF NOT EXISTS idx_athena_clients_source
    ON athena_clients (source)
    WHERE deleted_at IS NULL;

CREATE INDEX IF NOT EXISTS idx_athena_clients_active
    ON athena_clients (is_active, is_frozen)
    WHERE deleted_at IS NULL;

CREATE TABLE IF NOT EXISTS public_gateway_routes (
    id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
    created_at timestamptz NOT NULL DEFAULT now(),
    public_gateway_route_id uuid NOT NULL DEFAULT gen_random_uuid() UNIQUE,
    route_key text NOT NULL,
    client_name text NOT NULL,
    allowed_ops text[] NOT NULL,
    is_active boolean NOT NULL DEFAULT true,
    metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
    updated_at timestamptz NOT NULL DEFAULT now(),
    deleted_at timestamptz,
    PRIMARY KEY (id)
);

CREATE UNIQUE INDEX IF NOT EXISTS idx_public_gateway_routes_route_key_active
    ON public_gateway_routes (lower(route_key))
    WHERE deleted_at IS NULL;

CREATE INDEX IF NOT EXISTS idx_public_gateway_routes_client_name_active
    ON public_gateway_routes (lower(client_name))
    WHERE deleted_at IS NULL;

CREATE INDEX IF NOT EXISTS idx_public_gateway_routes_is_active
    ON public_gateway_routes (is_active)
    WHERE deleted_at IS NULL;

CREATE TABLE IF NOT EXISTS athena_client_configs (
    id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
    created_at timestamptz NOT NULL DEFAULT now(),
    athena_client_config_id uuid NOT NULL DEFAULT gen_random_uuid() UNIQUE,
    client_name text NOT NULL,
    config jsonb NOT NULL DEFAULT '{}'::jsonb,
    metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
    updated_at timestamptz NOT NULL DEFAULT now(),
    PRIMARY KEY (id)
);

CREATE UNIQUE INDEX IF NOT EXISTS idx_athena_client_configs_client_name
    ON athena_client_configs (lower(client_name));

-- ---------------------------------------------------------------------------
-- API registry (routing and configuration for external APIs)
-- ---------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS api_registry (
    id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,

    created_at timestamptz NOT NULL DEFAULT now(),
    api_registry_id uuid NOT NULL DEFAULT gen_random_uuid() UNIQUE,
    name text NOT NULL,
    description text,
    config jsonb NOT NULL DEFAULT '{}'::jsonb,
    updated_at timestamptz NOT NULL DEFAULT now(),
    PRIMARY KEY (id)
);

CREATE UNIQUE INDEX IF NOT EXISTS idx_api_registry_api_registry_id
    ON api_registry (api_registry_id);

CREATE INDEX IF NOT EXISTS idx_api_registry_updated_at
    ON api_registry (updated_at DESC);

-- ---------------------------------------------------------------------------
-- Client statistics
-- ---------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS client_statistics (
    id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
    created_at timestamptz NOT NULL DEFAULT now(),
    client_name text NOT NULL,
    total_requests bigint NOT NULL DEFAULT 0,
    successful_requests bigint NOT NULL DEFAULT 0,
    failed_requests bigint NOT NULL DEFAULT 0,
    total_cached_requests bigint NOT NULL DEFAULT 0,
    total_operations bigint NOT NULL DEFAULT 0,
    avg_request_duration_ms double precision,
    p50_request_duration_ms double precision,
    p95_request_duration_ms double precision,
    p99_request_duration_ms double precision,
    cache_hit_ratio double precision,
    last_request_at timestamptz,
    last_operation_at timestamptz,
    updated_at timestamptz NOT NULL DEFAULT now(),

    CONSTRAINT client_statistics_client_name_unique UNIQUE (client_name)
);

CREATE TABLE IF NOT EXISTS client_table_statistics (
    id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
    created_at timestamptz NOT NULL DEFAULT now(),
    client_name text NOT NULL,
    table_name text NOT NULL,
    operation text NOT NULL,
    total_operations bigint NOT NULL DEFAULT 0,
    error_operations bigint NOT NULL DEFAULT 0,
    avg_duration_ms double precision,
    p50_duration_ms double precision,
    p95_duration_ms double precision,
    p99_duration_ms double precision,
    total_cache_hits bigint NOT NULL DEFAULT 0,
    total_cache_misses bigint NOT NULL DEFAULT 0,
    cache_hit_ratio double precision,
    last_operation_at timestamptz,
    updated_at timestamptz NOT NULL DEFAULT now(),
    CONSTRAINT client_table_statistics_unique UNIQUE (client_name, table_name, operation)
);


CREATE INDEX IF NOT EXISTS idx_client_table_statistics_client_name
    ON client_table_statistics (client_name);

CREATE INDEX IF NOT EXISTS idx_client_table_statistics_table_name
    ON client_table_statistics (table_name);

CREATE TABLE IF NOT EXISTS client_alert_queries (
    id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    created_at timestamptz NOT NULL DEFAULT now(),
    client_name text,
    summary text,
    db_statement text,
    elapsed_secs numeric,
    slow_threshold_secs numeric,
    rows_affected bigint,
    rows_returned bigint
);

CREATE INDEX IF NOT EXISTS idx_client_alert_queries_created_at
    ON client_alert_queries (created_at DESC);

CREATE INDEX IF NOT EXISTS idx_client_alert_queries_client_name
    ON client_alert_queries (client_name)
    WHERE client_name IS NOT NULL;

CREATE TABLE IF NOT EXISTS query_optimization_runs (
    id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    created_at timestamptz NOT NULL DEFAULT now(),
    started_at timestamptz NOT NULL DEFAULT now(),
    completed_at timestamptz,
    client_name text NOT NULL,
    trigger_source text NOT NULL DEFAULT 'manual',
    status text NOT NULL DEFAULT 'running',
    recommendations_generated integer NOT NULL DEFAULT 0,
    error_message text,
    meta jsonb NOT NULL DEFAULT '{}'::jsonb,
    CONSTRAINT query_optimization_runs_status_check CHECK (
        status IN ('running', 'completed', 'failed')
    )
);

CREATE INDEX IF NOT EXISTS idx_query_optimization_runs_client_created
    ON query_optimization_runs (client_name, created_at DESC);

CREATE INDEX IF NOT EXISTS idx_query_optimization_runs_status
    ON query_optimization_runs (status, created_at DESC);

CREATE TABLE IF NOT EXISTS query_optimization_recommendations (
    id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    created_at timestamptz NOT NULL DEFAULT now(),
    updated_at timestamptz NOT NULL DEFAULT now(),
    client_name text NOT NULL,
    recommendation_key text NOT NULL,
    title text NOT NULL,
    recommendation_type text NOT NULL DEFAULT 'index',
    severity text NOT NULL DEFAULT 'medium',
    status text NOT NULL DEFAULT 'open',
    confidence double precision,
    impact_score double precision,
    rationale text,
    proposed_sql text,
    explain_plan jsonb,
    metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
    first_seen_at timestamptz NOT NULL DEFAULT now(),
    last_seen_at timestamptz NOT NULL DEFAULT now(),
    applied_at timestamptz,
    applied_by text,
    apply_error text,
    run_id bigint REFERENCES query_optimization_runs(id) ON DELETE SET NULL,
    CONSTRAINT query_optimization_recommendations_key_unique UNIQUE (client_name, recommendation_key),
    CONSTRAINT query_optimization_recommendations_severity_check CHECK (
        severity IN ('low', 'medium', 'high', 'critical')
    ),
    CONSTRAINT query_optimization_recommendations_status_check CHECK (
        status IN ('open', 'applied', 'dismissed')
    )
);

CREATE INDEX IF NOT EXISTS idx_query_optimization_recommendations_client_status
    ON query_optimization_recommendations (client_name, status, updated_at DESC);

CREATE INDEX IF NOT EXISTS idx_query_optimization_recommendations_severity
    ON query_optimization_recommendations (severity, updated_at DESC);

CREATE TABLE IF NOT EXISTS query_optimization_actions (
    id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    created_at timestamptz NOT NULL DEFAULT now(),
    completed_at timestamptz,
    recommendation_id bigint NOT NULL REFERENCES query_optimization_recommendations(id) ON DELETE CASCADE,
    client_name text NOT NULL,
    action text NOT NULL DEFAULT 'apply',
    status text NOT NULL DEFAULT 'running',
    actor text,
    executed_sql text,
    error_message text,
    meta jsonb NOT NULL DEFAULT '{}'::jsonb,
    CONSTRAINT query_optimization_actions_status_check CHECK (
        status IN ('running', 'success', 'failed', 'skipped')
    )
);

CREATE INDEX IF NOT EXISTS idx_query_optimization_actions_recommendation
    ON query_optimization_actions (recommendation_id, created_at DESC);

CREATE INDEX IF NOT EXISTS idx_query_optimization_actions_client_created
    ON query_optimization_actions (client_name, created_at DESC);

-- ---------------------------------------------------------------------------
-- Connection pool occupancy snapshots
-- ---------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS client_connections (
    id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    recorded_at timestamptz NOT NULL DEFAULT now(),
    client_name text NOT NULL,
    host text,
    instance_id uuid NOT NULL,
    pool_size integer NOT NULL,
    idle_connections integer NOT NULL,
    active_connections integer NOT NULL,
    max_connections integer NOT NULL
);

CREATE INDEX IF NOT EXISTS idx_client_connections_client_recorded
    ON client_connections (client_name, recorded_at DESC);

CREATE INDEX IF NOT EXISTS idx_client_connections_instance
    ON client_connections (instance_id);

-- ---------------------------------------------------------------------------
-- Vacuum health (per-target-client snapshots stored in logging DB)
-- ---------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS vacuum_health_snapshots (
    id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    recorded_at timestamptz NOT NULL DEFAULT now(),
    client_name text NOT NULL,
    host text,
    instance_id uuid,
    total_dead_rows bigint NOT NULL DEFAULT 0,
    tables_with_bloat integer NOT NULL DEFAULT 0,
    xid_freeze_risk integer NOT NULL DEFAULT 0,
    tables_needing_vacuum integer NOT NULL DEFAULT 0,
    freeze_max_age bigint,
    collection_error text
);

CREATE INDEX IF NOT EXISTS idx_vacuum_health_snapshots_client_recorded
    ON vacuum_health_snapshots (client_name, recorded_at DESC);

CREATE TABLE IF NOT EXISTS vacuum_health_table_stats (
    id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    snapshot_id bigint NOT NULL REFERENCES vacuum_health_snapshots(id) ON DELETE CASCADE,
    schemaname text NOT NULL,
    relname text NOT NULL,
    n_dead_tup bigint NOT NULL DEFAULT 0,
    n_live_tup bigint NOT NULL DEFAULT 0,
    dead_pct numeric,
    last_vacuum timestamptz,
    last_autovacuum timestamptz,
    xid_age bigint,
    xid_age_pct_of_freeze_max numeric
);

CREATE INDEX IF NOT EXISTS idx_vacuum_health_table_stats_snapshot
    ON vacuum_health_table_stats (snapshot_id);

-- ---------------------------------------------------------------------------
-- Query history and saved queries
-- ---------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS public.query_history (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    query TEXT NOT NULL,
    client_name TEXT,
    status TEXT NOT NULL DEFAULT 'success',
    execution_time_ms DOUBLE PRECISION,
    error_message TEXT,
    row_count INTEGER,
    executed_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_query_history_client
    ON public.query_history(client_name);

CREATE INDEX IF NOT EXISTS idx_query_history_executed
    ON public.query_history(executed_at DESC);

CREATE TABLE IF NOT EXISTS public.saved_queries (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    name TEXT NOT NULL,
    description TEXT,
    query TEXT NOT NULL,
    client_name TEXT,
    schema_name TEXT DEFAULT 'public',
    tags TEXT[] DEFAULT '{}',
    created_by TEXT,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_saved_queries_client
    ON public.saved_queries(client_name);

CREATE INDEX IF NOT EXISTS idx_saved_queries_created
    ON public.saved_queries(created_at DESC);

-- ---------------------------------------------------------------------------
-- UI request log (used by the Web Explorer)
-- ---------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS public.ui_request_log (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    method TEXT NOT NULL,
    path TEXT NOT NULL,
    athena_url TEXT NOT NULL,
    client_name TEXT,
    status_code INTEGER,
    duration_ms DOUBLE PRECISION,
    error_message TEXT,
    request_body JSONB,
    response_preview TEXT,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_ui_request_log_created
    ON public.ui_request_log(created_at DESC);

CREATE INDEX IF NOT EXISTS idx_ui_request_log_client
    ON public.ui_request_log(client_name);

CREATE INDEX IF NOT EXISTS idx_ui_request_log_path
    ON public.ui_request_log(path);

-- ---------------------------------------------------------------------------
-- Frontend intake tables (feedback, organization requests, project requests)
-- ---------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS public.feedback (
    id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    created_at timestamptz NOT NULL DEFAULT now(),
    category text NOT NULL DEFAULT 'issue',
    title text,
    message text NOT NULL,
    page text,
    email text,
    client_name text,
    organization_name text,
    metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
    status text NOT NULL DEFAULT 'new'
);

CREATE INDEX IF NOT EXISTS idx_feedback_created_at
    ON public.feedback(created_at DESC);

CREATE INDEX IF NOT EXISTS idx_feedback_status
    ON public.feedback(status);

CREATE TABLE IF NOT EXISTS public.organization_requests (
    id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    created_at timestamptz NOT NULL DEFAULT now(),
    organization_name text NOT NULL,
    organization_type text,
    plan text,
    requested_by text,
    notes text,
    client_name text,
    metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
    status text NOT NULL DEFAULT 'new'
);

CREATE INDEX IF NOT EXISTS idx_organization_requests_created_at
    ON public.organization_requests(created_at DESC);

CREATE INDEX IF NOT EXISTS idx_organization_requests_status
    ON public.organization_requests(status);

CREATE TABLE IF NOT EXISTS public.project_requests (
    id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    created_at timestamptz NOT NULL DEFAULT now(),
    organization_name text,
    project_name text NOT NULL,
    region text,
    enable_data_api boolean NOT NULL DEFAULT true,
    enable_automatic_rls boolean NOT NULL DEFAULT false,
    requested_by text,
    client_name text,
    metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
    status text NOT NULL DEFAULT 'new'
);

CREATE INDEX IF NOT EXISTS idx_project_requests_created_at
    ON public.project_requests(created_at DESC);

CREATE INDEX IF NOT EXISTS idx_project_requests_status
    ON public.project_requests(status);

-- ---------------------------------------------------------------------------
-- Backup job tracking (used by /admin/backups)
-- ---------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS backup_jobs (
    id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    job_type text NOT NULL, -- 'backup' | 'restore'
    client_name text,
    status text NOT NULL, -- 'pending' | 'running' | 'completed' | 'failed' | 'cancelled'
    progress_pct integer,
    progress_stage text,
    s3_bucket text,
    s3_key text,
    label text,
    size_bytes bigint,
    error_message text,
    started_at timestamptz NOT NULL DEFAULT now(),
    updated_at timestamptz NOT NULL DEFAULT now(),
    completed_at timestamptz
);

CREATE INDEX IF NOT EXISTS idx_backup_jobs_client_started_at
    ON backup_jobs (client_name, started_at DESC);

CREATE INDEX IF NOT EXISTS idx_backup_jobs_status
    ON backup_jobs (status);

CREATE TABLE IF NOT EXISTS backup_job_logs (
    id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    job_id bigint NOT NULL REFERENCES backup_jobs(id) ON DELETE CASCADE,
    level text NOT NULL, -- 'info' | 'warn' | 'error'
    message text NOT NULL,
    created_at timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_backup_job_logs_job_created_at
    ON backup_job_logs (job_id, created_at);

-- ── backup_schedules ─────────────────────────────────────────────────────────

CREATE TABLE IF NOT EXISTS backup_schedules (
    id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    client_name text NOT NULL,
    pg_uri text,
    frequency text NOT NULL CHECK (frequency IN ('hourly', 'daily', 'weekly', 'monthly')),
    cron_expression text NOT NULL,
    time_of_day time NOT NULL DEFAULT '02:00',
    day_of_week integer CHECK (day_of_week IS NULL OR (day_of_week >= 0 AND day_of_week <= 6)),
    day_of_month integer CHECK (day_of_month IS NULL OR (day_of_month >= 1 AND day_of_month <= 28)),
    label text,
    enabled boolean NOT NULL DEFAULT true,
    timeout_seconds integer NOT NULL DEFAULT 3600,
    last_run_at timestamptz,
    last_job_id bigint REFERENCES backup_jobs(id),
    next_run_at timestamptz,
    pg_cron_job_id bigint,
    created_at timestamptz NOT NULL DEFAULT now(),
    updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_backup_schedules_client
    ON backup_schedules (client_name);

CREATE INDEX IF NOT EXISTS idx_backup_schedules_enabled
    ON backup_schedules (enabled) WHERE enabled = true;


CREATE TABLE IF NOT EXISTS public.storage_profiles (
    id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    created_at timestamptz NOT NULL DEFAULT now(),
    name text NOT NULL,
    provider text NOT NULL DEFAULT 'aws-s3',
    endpoint text NOT NULL,
    region text NOT NULL DEFAULT 'us-east-1',
    bucket text NOT NULL,
    access_key_id text,
    secret_key text,
    metadata jsonb NOT NULL DEFAULT '{}'::jsonb
);

CREATE UNIQUE INDEX IF NOT EXISTS idx_storage_profiles_name
    ON public.storage_profiles(name);

CREATE INDEX IF NOT EXISTS idx_storage_profiles_created_at
    ON public.storage_profiles(created_at DESC);

-- ---------------------------------------------------------------------------
-- Frontend intake tables (feedback, organization requests, project requests)
-- ---------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS public.feedback (
    id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    created_at timestamptz NOT NULL DEFAULT now(),
    category text NOT NULL DEFAULT 'issue',
    title text,
    message text NOT NULL,
    page text,
    email text,
    client_name text,
    organization_name text,
    metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
    status text NOT NULL DEFAULT 'new'
);

CREATE INDEX IF NOT EXISTS idx_feedback_created_at
    ON public.feedback(created_at DESC);

CREATE INDEX IF NOT EXISTS idx_feedback_status
    ON public.feedback(status);

CREATE TABLE IF NOT EXISTS public.organization_requests (
    id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    created_at timestamptz NOT NULL DEFAULT now(),
    organization_name text NOT NULL,
    organization_type text,
    plan text,
    requested_by text,
    notes text,
    client_name text,
    metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
    status text NOT NULL DEFAULT 'new'
);

CREATE INDEX IF NOT EXISTS idx_organization_requests_created_at
    ON public.organization_requests(created_at DESC);

CREATE INDEX IF NOT EXISTS idx_organization_requests_status
    ON public.organization_requests(status);

CREATE TABLE IF NOT EXISTS public.project_requests (
    id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    created_at timestamptz NOT NULL DEFAULT now(),
    organization_name text,
    project_name text NOT NULL,
    region text,
    enable_data_api boolean NOT NULL DEFAULT true,
    enable_automatic_rls boolean NOT NULL DEFAULT false,
    requested_by text,
    client_name text,
    metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
    status text NOT NULL DEFAULT 'new'
);

CREATE INDEX IF NOT EXISTS idx_project_requests_created_at
    ON public.project_requests(created_at DESC);

CREATE INDEX IF NOT EXISTS idx_project_requests_status
    ON public.project_requests(status);

-- ---------------------------------------------------------------------------
-- Gateway webhooks (definitions + delivery log)
-- ---------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS gateway_webhook (
    id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    created_at timestamptz NOT NULL DEFAULT now(),
    updated_at timestamptz NOT NULL DEFAULT now(),
    enabled boolean NOT NULL DEFAULT true,
    name text NOT NULL,
    description text,
    athena_base_url text,
    client_name text NOT NULL,
    table_name text,
    route_key text NOT NULL,
    http_method text NOT NULL,
    url_template text NOT NULL,
    headers_templates jsonb NOT NULL DEFAULT '{}'::jsonb,
    body_template text,
    timeout_ms integer NOT NULL DEFAULT 30000,
    include_request_body_in_context boolean NOT NULL DEFAULT true,
    lookup_keys jsonb NOT NULL DEFAULT '{}'::jsonb,
    slug text,
    athena_base_url_norm text GENERATED ALWAYS AS (COALESCE(athena_base_url, '')) STORED,
    table_name_norm text GENERATED ALWAYS AS (COALESCE(table_name, '')) STORED,
    CONSTRAINT gateway_webhook_route_key_check CHECK (route_key IN (
        'gateway_data', 'gateway_fetch', 'gateway_update', 'gateway_query',
        'gateway_insert', 'gateway_delete',
        'rest_get', 'rest_post', 'rest_patch', 'rest_delete',
        'schema_table_created', 'schema_table_dropped',
        'schema_column_added', 'schema_column_dropped', 'schema_column_renamed', 'schema_column_altered',
        'schema_index_created', 'schema_index_dropped', 'schema_extension_installed'
    )),
    CONSTRAINT gateway_webhook_method_check CHECK (http_method IN (
        'GET', 'POST', 'PUT', 'PATCH', 'DELETE'
    )),
    CONSTRAINT gateway_webhook_timeout_ms_check CHECK (timeout_ms >= 1000 AND timeout_ms <= 300000)
);

CREATE UNIQUE INDEX IF NOT EXISTS idx_gateway_webhook_slug
    ON gateway_webhook (slug)
    WHERE slug IS NOT NULL AND slug <> '';

CREATE UNIQUE INDEX IF NOT EXISTS idx_gateway_webhook_natural_identity
    ON gateway_webhook (
        client_name,
        route_key,
        http_method,
        url_template,
        athena_base_url_norm,
        table_name_norm
    );

CREATE INDEX IF NOT EXISTS idx_gateway_webhook_client_route
    ON gateway_webhook (client_name, route_key, enabled);

CREATE TABLE IF NOT EXISTS gateway_webhook_delivery (
    id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    created_at timestamptz NOT NULL DEFAULT now(),
    webhook_id bigint NOT NULL REFERENCES gateway_webhook(id) ON DELETE CASCADE,
    trigger_route_key text NOT NULL,
    request_id text,
    idempotency_key text NOT NULL,
    status text NOT NULL,
    http_status integer,
    response_headers_json jsonb,
    response_body_snippet text,
    error_message text,
    duration_ms bigint,
    resolved_url text,
    context_snapshot jsonb NOT NULL DEFAULT '{}'::jsonb,
    CONSTRAINT gateway_webhook_delivery_status_check CHECK (status IN (
        'pending', 'success', 'failed', 'skipped'
    )),
    CONSTRAINT gateway_webhook_delivery_idempotency_key_unique UNIQUE (idempotency_key)
);

CREATE INDEX IF NOT EXISTS idx_gateway_webhook_delivery_webhook_created
    ON gateway_webhook_delivery (webhook_id, created_at DESC);

CREATE INDEX IF NOT EXISTS idx_gateway_webhook_delivery_status_created
    ON gateway_webhook_delivery (status, created_at DESC);
