-- EvoBGP initial schema (PostgreSQL). See plan §5.
-- UUIDs: application may use UUID v7/ULID as text-compatible UUID.

CREATE TABLE tenant (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    name        TEXT NOT NULL,
    slug        TEXT NOT NULL,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    CONSTRAINT tenant_slug_chk CHECK (length(trim(slug)) > 0),
    CONSTRAINT tenant_slug_uniq UNIQUE (slug)
);

CREATE TABLE doh_profile (
    id           UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id    UUID 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   TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at   TIMESTAMPTZ NOT NULL DEFAULT now(),
    CONSTRAINT doh_profile_url_chk CHECK (length(trim(url)) > 0)
);

CREATE INDEX idx_doh_profile_tenant ON doh_profile (tenant_id);

CREATE TABLE bgp_community (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID NOT NULL REFERENCES tenant (id) ON DELETE CASCADE,
    name        TEXT NOT NULL DEFAULT '',
    kind        TEXT NOT NULL,
    value_json  JSONB NOT NULL DEFAULT '{}',
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    CONSTRAINT bgp_community_kind_chk CHECK (length(trim(kind)) > 0)
);

CREATE INDEX idx_bgp_community_tenant ON bgp_community (tenant_id);

CREATE TABLE module (
    id                    UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id             UUID NOT NULL REFERENCES tenant (id) ON DELETE CASCADE,
    type                  TEXT NOT NULL,
    name                  TEXT NOT NULL,
    enabled               BOOLEAN NOT NULL DEFAULT true,
    priority              INTEGER NOT NULL DEFAULT 0,
    doh_profile_id        UUID REFERENCES doh_profile (id) ON DELETE SET NULL,
    refresh_interval_sec  INTEGER,
    cron_expr             TEXT,
    default_community_id  UUID REFERENCES bgp_community (id) ON DELETE SET NULL,
    deleted_at            TIMESTAMPTZ,
    created_at            TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at            TIMESTAMPTZ NOT NULL DEFAULT now(),
    CONSTRAINT module_type_chk CHECK (type IN (
        'AS_PREFIXES', 'CDN_CIDRS', 'DOMAINS', 'IP_RANGES'
    )),
    CONSTRAINT module_name_chk 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                    UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    module_id             UUID 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          UUID REFERENCES bgp_community (id) ON DELETE SET NULL,
    created_at            TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at            TIMESTAMPTZ NOT NULL DEFAULT now(),
    CONSTRAINT module_cdn_source_url_chk CHECK (length(trim(url)) > 0)
);

CREATE INDEX idx_module_cdn_source_module ON module_cdn_source (module_id);

CREATE TABLE module_domain_entry (
    id            UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    module_id     UUID NOT NULL REFERENCES module (id) ON DELETE CASCADE,
    fqdn          TEXT NOT NULL,
    community_id  UUID REFERENCES bgp_community (id) ON DELETE SET NULL,
    resolve_meta  JSONB NOT NULL DEFAULT '{}',
    created_at    TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at    TIMESTAMPTZ NOT NULL DEFAULT now(),
    CONSTRAINT module_domain_fqdn_chk CHECK (length(trim(fqdn)) > 0)
);

CREATE INDEX idx_module_domain_entry_module ON module_domain_entry (module_id);

CREATE TABLE module_as_entry (
    id            UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    module_id     UUID NOT NULL REFERENCES module (id) ON DELETE CASCADE,
    asn           BIGINT,
    prefix        CIDR,
    community_id  UUID REFERENCES bgp_community (id) ON DELETE SET NULL,
    created_at    TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at    TIMESTAMPTZ NOT NULL DEFAULT now(),
    CONSTRAINT module_as_entry_asn_or_prefix_chk 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            UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    module_id     UUID NOT NULL REFERENCES module (id) ON DELETE CASCADE,
    prefix        CIDR NOT NULL,
    community_id  UUID REFERENCES bgp_community (id) ON DELETE SET NULL,
    created_at    TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at    TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE INDEX idx_module_ip_range_entry_module ON module_ip_range_entry (module_id);

-- Full config snapshot for a tenant (optionally tied to one module for filtering in API).
CREATE TABLE config_revision (
    id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id           UUID NOT NULL REFERENCES tenant (id) ON DELETE CASCADE,
    module_id           UUID REFERENCES module (id) ON DELETE SET NULL,
    content_hash        TEXT NOT NULL,
    parent_revision_id  UUID REFERENCES config_revision (id) ON DELETE SET NULL,
    artifact_ref        TEXT,
    meta_json           JSONB NOT NULL DEFAULT '{}',
    created_at          TIMESTAMPTZ NOT NULL DEFAULT now(),
    CONSTRAINT config_revision_hash_chk 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                         UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id                  UUID NOT NULL REFERENCES tenant (id) ON DELETE CASCADE,
    role                       TEXT NOT NULL,
    endpoint                   TEXT,
    last_applied_revision_id   UUID REFERENCES config_revision (id) ON DELETE SET NULL,
    meta_json                  JSONB NOT NULL DEFAULT '{}',
    created_at                 TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at                 TIMESTAMPTZ NOT NULL DEFAULT now(),
    CONSTRAINT bgp_speaker_role_chk CHECK (role IN ('master', 'replica'))
);

CREATE INDEX idx_bgp_speaker_tenant ON bgp_speaker (tenant_id);

CREATE TABLE bgp_peer (
    id               UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id        UUID NOT NULL REFERENCES tenant (id) ON DELETE CASCADE,
    bgp_speaker_id   UUID REFERENCES bgp_speaker (id) ON DELETE SET NULL,
    neighbor         INET NOT NULL,
    remote_asn       BIGINT NOT NULL,
    enabled          BOOLEAN NOT NULL DEFAULT true,
    policies_json    JSONB NOT NULL DEFAULT '{}',
    meta_json        JSONB NOT NULL DEFAULT '{}',
    created_at       TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at       TIMESTAMPTZ NOT NULL DEFAULT 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            BIGSERIAL PRIMARY KEY,
    revision_id   UUID NOT NULL REFERENCES config_revision (id) ON DELETE CASCADE,
    prefix        CIDR NOT NULL,
    community_id  UUID REFERENCES bgp_community (id) ON DELETE SET NULL,
    source        TEXT NOT NULL DEFAULT '',
    meta_json     JSONB 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                 UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id          UUID NOT NULL REFERENCES tenant (id) ON DELETE CASCADE,
    kind               TEXT NOT NULL,
    status             TEXT NOT NULL,
    idempotency_key    TEXT,
    module_id          UUID REFERENCES module (id) ON DELETE SET NULL,
    error_message      TEXT,
    progress_pct       SMALLINT,
    meta_json          JSONB NOT NULL DEFAULT '{}',
    created_at         TIMESTAMPTZ NOT NULL DEFAULT now(),
    started_at         TIMESTAMPTZ,
    finished_at        TIMESTAMPTZ,
    CONSTRAINT job_audit_status_chk CHECK (status IN (
        'queued', 'running', 'succeeded', 'failed', 'cancelled'
    )),
    CONSTRAINT job_audit_kind_chk 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  UUID NOT NULL REFERENCES tenant (id) ON DELETE CASCADE,
    key        TEXT NOT NULL,
    value_json JSONB NOT NULL DEFAULT '{}',
    updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    PRIMARY KEY (tenant_id, key),
    CONSTRAINT global_settings_key_chk CHECK (length(trim(key)) > 0)
);

-- Optional CDN fetch audit (plan §5.4).
CREATE TABLE module_cdn_fetch_log (
    id          BIGSERIAL PRIMARY KEY,
    module_id   UUID NOT NULL REFERENCES module (id) ON DELETE CASCADE,
    source_id   UUID REFERENCES module_cdn_source (id) ON DELETE SET NULL,
    http_status INTEGER,
    bytes       BIGINT,
    error       TEXT,
    fetched_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);

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