-- +goose Up
-- +goose StatementBegin

-- Upstream backends (target servers behind the reverse-proxy).
CREATE TABLE IF NOT EXISTS backends (
    id                 BIGSERIAL PRIMARY KEY,
    name               TEXT NOT NULL,
    scheme             TEXT NOT NULL DEFAULT 'http',
    address            TEXT NOT NULL,
    port               INTEGER NOT NULL,
    health_check_path  TEXT,
    active             BOOLEAN NOT NULL DEFAULT TRUE,
    created_at         TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at         TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    CONSTRAINT backends_name_unique UNIQUE (name),
    CONSTRAINT backends_scheme_check CHECK (scheme IN ('http', 'https')),
    CONSTRAINT backends_port_check   CHECK (port > 0 AND port < 65536)
);

CREATE INDEX IF NOT EXISTS idx_backends_active ON backends (active) WHERE active;

-- Public-facing domains. primary_backend_id is the default upstream
-- when no path-rule matches.
CREATE TABLE IF NOT EXISTS domains (
    id                  BIGSERIAL PRIMARY KEY,
    name                TEXT NOT NULL,
    active              BOOLEAN NOT NULL DEFAULT TRUE,
    primary_backend_id  BIGINT REFERENCES backends(id) ON DELETE SET NULL,
    http_to_https       BOOLEAN NOT NULL DEFAULT TRUE,
    hsts_enabled        BOOLEAN NOT NULL DEFAULT FALSE,
    notes               TEXT,
    created_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    CONSTRAINT domains_name_unique UNIQUE (name)
);

CREATE INDEX IF NOT EXISTS idx_domains_active ON domains (active) WHERE active;

-- Path-based routing rules. Higher priority wins; ties broken by id.
CREATE TABLE IF NOT EXISTS routing_rules (
    id           BIGSERIAL PRIMARY KEY,
    domain_id    BIGINT NOT NULL REFERENCES domains(id) ON DELETE CASCADE,
    path_prefix  TEXT NOT NULL DEFAULT '/',
    backend_id   BIGINT NOT NULL REFERENCES backends(id) ON DELETE RESTRICT,
    priority     INTEGER NOT NULL DEFAULT 100,
    active       BOOLEAN NOT NULL DEFAULT TRUE,
    created_at   TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at   TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_routing_rules_domain   ON routing_rules (domain_id);
CREATE INDEX IF NOT EXISTS idx_routing_rules_priority ON routing_rules (domain_id, priority DESC);

-- ACME-managed TLS certificates. status mirrors certbot's view:
--   pending  — issue requested, not yet completed
--   active   — issued, currently deployed
--   renewing — renewal in progress
--   expired  — past not_after, awaiting cleanup
--   error    — last attempt failed (see audit_log for detail)
CREATE TABLE IF NOT EXISTS tls_certs (
    id               BIGSERIAL PRIMARY KEY,
    domain           TEXT NOT NULL,
    issuer           TEXT NOT NULL DEFAULT 'letsencrypt',
    status           TEXT NOT NULL DEFAULT 'pending',
    cert_path        TEXT,
    key_path         TEXT,
    not_before       TIMESTAMPTZ,
    not_after        TIMESTAMPTZ,
    last_renewed_at  TIMESTAMPTZ,
    last_error       TEXT,
    created_at       TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at       TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    CONSTRAINT tls_certs_domain_unique UNIQUE (domain),
    CONSTRAINT tls_certs_status_check  CHECK (status IN ('pending', 'active', 'renewing', 'expired', 'error'))
);

CREATE INDEX IF NOT EXISTS idx_tls_certs_not_after ON tls_certs (not_after);
CREATE INDEX IF NOT EXISTS idx_tls_certs_status   ON tls_certs (status);

-- +goose StatementEnd

-- +goose Down
-- +goose StatementBegin
DROP TABLE IF EXISTS tls_certs;
DROP TABLE IF EXISTS routing_rules;
DROP TABLE IF EXISTS domains;
DROP TABLE IF EXISTS backends;
-- +goose StatementEnd
