-- EvoBGP initial schema (SQLite, optional microVPS_sqlite / single-container).
-- UUIDs: supply from application (ULID/UUIDv7). JSON columns stored as TEXT.

PRAGMA foreign_keys = ON;

CREATE TABLE tenant (
    id          TEXT PRIMARY KEY NOT NULL,
    name        TEXT NOT NULL,
    slug        TEXT NOT NULL UNIQUE,
    created_at  TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
    updated_at  TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
    CHECK (length(trim(slug)) > 0)
);

CREATE TABLE doh_profile (
    id           TEXT PRIMARY KEY NOT NULL,
    tenant_id    TEXT NOT NULL REFERENCES tenant (id) ON DELETE CASCADE,
    name         TEXT NOT NULL DEFAULT '',
    url          TEXT NOT NULL,
    timeout_ms   INTEGER,
    secret_ref   TEXT,
    created_at   TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
    updated_at   TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
    CHECK (length(trim(url)) > 0)
);

CREATE INDEX idx_doh_profile_tenant ON doh_profile (tenant_id);

CREATE TABLE bgp_community (
    id          TEXT PRIMARY KEY NOT NULL,
    tenant_id   TEXT NOT NULL REFERENCES tenant (id) ON DELETE CASCADE,
    name        TEXT NOT NULL DEFAULT '',
    kind        TEXT NOT NULL,
    value_json  TEXT NOT NULL DEFAULT '{}',
    created_at  TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
    updated_at  TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
    CHECK (length(trim(kind)) > 0)
);

CREATE INDEX idx_bgp_community_tenant ON bgp_community (tenant_id);

CREATE TABLE module (
    id                    TEXT PRIMARY KEY NOT NULL,
    tenant_id             TEXT NOT NULL REFERENCES tenant (id) ON DELETE CASCADE,
    type                  TEXT NOT NULL,
    name                  TEXT NOT NULL,
    enabled               INTEGER NOT NULL DEFAULT 1,
    priority              INTEGER NOT NULL DEFAULT 0,
    doh_profile_id        TEXT REFERENCES doh_profile (id) ON DELETE SET NULL,
    refresh_interval_sec  INTEGER,
    cron_expr             TEXT,
    default_community_id  TEXT REFERENCES bgp_community (id) ON DELETE SET NULL,
    deleted_at            TEXT,
    created_at            TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
    updated_at            TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
    CHECK (type IN ('AS_PREFIXES', 'CDN_CIDRS', 'DOMAINS', 'IP_RANGES')),
    CHECK (length(trim(name)) > 0)
);

CREATE INDEX idx_module_tenant ON module (tenant_id);
CREATE INDEX idx_module_tenant_type ON module (tenant_id, type);
CREATE INDEX idx_module_tenant_enabled ON module (tenant_id, enabled) WHERE deleted_at IS NULL;

CREATE TABLE module_cdn_source (
    id                    TEXT PRIMARY KEY NOT NULL,
    module_id             TEXT NOT NULL REFERENCES module (id) ON DELETE CASCADE,
    source_kind           TEXT NOT NULL,
    url                   TEXT NOT NULL,
    etag                  TEXT,
    refresh_interval_sec  INTEGER,
    community_id          TEXT REFERENCES bgp_community (id) ON DELETE SET NULL,
    created_at            TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
    updated_at            TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
    CHECK (length(trim(url)) > 0)
);

CREATE INDEX idx_module_cdn_source_module ON module_cdn_source (module_id);

CREATE TABLE module_domain_entry (
    id            TEXT PRIMARY KEY NOT NULL,
    module_id     TEXT NOT NULL REFERENCES module (id) ON DELETE CASCADE,
    fqdn          TEXT NOT NULL,
    community_id  TEXT REFERENCES bgp_community (id) ON DELETE SET NULL,
    resolve_meta  TEXT NOT NULL DEFAULT '{}',
    created_at    TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
    updated_at    TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
    CHECK (length(trim(fqdn)) > 0)
);

CREATE INDEX idx_module_domain_entry_module ON module_domain_entry (module_id);

CREATE TABLE module_as_entry (
    id            TEXT PRIMARY KEY NOT NULL,
    module_id     TEXT NOT NULL REFERENCES module (id) ON DELETE CASCADE,
    asn           INTEGER,
    prefix        TEXT,
    community_id  TEXT REFERENCES bgp_community (id) ON DELETE SET NULL,
    created_at    TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
    updated_at    TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
    CHECK (asn IS NOT NULL OR prefix IS NOT NULL)
);

CREATE INDEX idx_module_as_entry_module ON module_as_entry (module_id);

CREATE TABLE module_ip_range_entry (
    id            TEXT PRIMARY KEY NOT NULL,
    module_id     TEXT NOT NULL REFERENCES module (id) ON DELETE CASCADE,
    prefix        TEXT NOT NULL,
    community_id  TEXT REFERENCES bgp_community (id) ON DELETE SET NULL,
    created_at    TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
    updated_at    TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
);

CREATE INDEX idx_module_ip_range_entry_module ON module_ip_range_entry (module_id);

CREATE TABLE config_revision (
    id                  TEXT PRIMARY KEY NOT NULL,
    tenant_id           TEXT NOT NULL REFERENCES tenant (id) ON DELETE CASCADE,
    module_id           TEXT REFERENCES module (id) ON DELETE SET NULL,
    content_hash        TEXT NOT NULL,
    parent_revision_id  TEXT REFERENCES config_revision (id) ON DELETE SET NULL,
    artifact_ref        TEXT,
    meta_json           TEXT NOT NULL DEFAULT '{}',
    created_at          TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
    CHECK (length(trim(content_hash)) > 0)
);

CREATE INDEX idx_config_revision_tenant ON config_revision (tenant_id, created_at DESC);
CREATE INDEX idx_config_revision_module ON config_revision (module_id) WHERE module_id IS NOT NULL;

CREATE TABLE bgp_speaker (
    id                         TEXT PRIMARY KEY NOT NULL,
    tenant_id                  TEXT NOT NULL REFERENCES tenant (id) ON DELETE CASCADE,
    role                       TEXT NOT NULL,
    endpoint                   TEXT,
    last_applied_revision_id   TEXT REFERENCES config_revision (id) ON DELETE SET NULL,
    meta_json                  TEXT NOT NULL DEFAULT '{}',
    created_at                 TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
    updated_at                 TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
    CHECK (role IN ('master', 'replica'))
);

CREATE INDEX idx_bgp_speaker_tenant ON bgp_speaker (tenant_id);

CREATE TABLE bgp_peer (
    id               TEXT PRIMARY KEY NOT NULL,
    tenant_id        TEXT NOT NULL REFERENCES tenant (id) ON DELETE CASCADE,
    bgp_speaker_id   TEXT REFERENCES bgp_speaker (id) ON DELETE SET NULL,
    neighbor         TEXT NOT NULL,
    remote_asn       INTEGER NOT NULL,
    enabled          INTEGER NOT NULL DEFAULT 1,
    policies_json    TEXT NOT NULL DEFAULT '{}',
    meta_json        TEXT NOT NULL DEFAULT '{}',
    created_at       TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
    updated_at       TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
);

CREATE INDEX idx_bgp_peer_tenant ON bgp_peer (tenant_id);
CREATE INDEX idx_bgp_peer_speaker ON bgp_peer (bgp_speaker_id);

CREATE TABLE revision_materialized_prefix (
    id            INTEGER PRIMARY KEY AUTOINCREMENT,
    revision_id   TEXT NOT NULL REFERENCES config_revision (id) ON DELETE CASCADE,
    prefix        TEXT NOT NULL,
    community_id  TEXT REFERENCES bgp_community (id) ON DELETE SET NULL,
    source        TEXT NOT NULL DEFAULT '',
    meta_json     TEXT NOT NULL DEFAULT '{}'
);

CREATE INDEX idx_rev_mat_prefix_revision ON revision_materialized_prefix (revision_id);
CREATE INDEX idx_rev_mat_prefix_value ON revision_materialized_prefix (prefix);

CREATE TABLE job_audit (
    id                 TEXT PRIMARY KEY NOT NULL,
    tenant_id          TEXT NOT NULL REFERENCES tenant (id) ON DELETE CASCADE,
    kind               TEXT NOT NULL,
    status             TEXT NOT NULL,
    idempotency_key    TEXT,
    module_id          TEXT REFERENCES module (id) ON DELETE SET NULL,
    error_message      TEXT,
    progress_pct       INTEGER,
    meta_json          TEXT NOT NULL DEFAULT '{}',
    created_at         TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
    started_at         TEXT,
    finished_at        TEXT,
    CHECK (status IN ('queued', 'running', 'succeeded', 'failed', 'cancelled')),
    CHECK (length(trim(kind)) > 0)
);

CREATE INDEX idx_job_audit_tenant_created ON job_audit (tenant_id, created_at DESC);
CREATE INDEX idx_job_audit_tenant_status ON job_audit (tenant_id, status);
CREATE UNIQUE INDEX idx_job_audit_idempotency ON job_audit (tenant_id, idempotency_key)
    WHERE idempotency_key IS NOT NULL;

CREATE TABLE global_settings (
    tenant_id  TEXT NOT NULL REFERENCES tenant (id) ON DELETE CASCADE,
    key        TEXT NOT NULL,
    value_json TEXT NOT NULL DEFAULT '{}',
    updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
    PRIMARY KEY (tenant_id, key),
    CHECK (length(trim(key)) > 0)
);

CREATE TABLE module_cdn_fetch_log (
    id          INTEGER PRIMARY KEY AUTOINCREMENT,
    module_id   TEXT NOT NULL REFERENCES module (id) ON DELETE CASCADE,
    source_id   TEXT REFERENCES module_cdn_source (id) ON DELETE SET NULL,
    http_status INTEGER,
    bytes       INTEGER,
    error       TEXT,
    fetched_at  TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
);

CREATE INDEX idx_module_cdn_fetch_log_module ON module_cdn_fetch_log (module_id, fetched_at DESC);
