CREATE TABLE IF NOT EXISTS gateway.prompt_categories (
    id uuid PRIMARY KEY,
    name text NOT NULL UNIQUE,
    description text NOT NULL DEFAULT '',
    created_at timestamptz NOT NULL DEFAULT clock_timestamp()
);

CREATE TABLE IF NOT EXISTS gateway.prompt_templates (
    id uuid PRIMARY KEY,
    name text NOT NULL UNIQUE,
    description text NOT NULL DEFAULT '',
    category_id uuid REFERENCES gateway.prompt_categories(id) ON DELETE SET NULL,
    tags text[] NOT NULL DEFAULT '{}',
    department_ids uuid[] NOT NULL DEFAULT '{}',
    enabled boolean NOT NULL DEFAULT true,
    current_version integer,
    revision bigint NOT NULL DEFAULT 1,
    created_by uuid REFERENCES gateway.admin_accounts(id),
    created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
    updated_at timestamptz NOT NULL DEFAULT clock_timestamp()
);

CREATE TABLE IF NOT EXISTS gateway.prompt_versions (
    id uuid PRIMARY KEY,
    template_id uuid NOT NULL REFERENCES gateway.prompt_templates(id) ON DELETE CASCADE,
    version integer NOT NULL CHECK (version > 0),
    content text NOT NULL CHECK (length(content) BETWEEN 1 AND 100000),
    variables jsonb NOT NULL DEFAULT '[]'::jsonb CHECK (jsonb_typeof(variables) = 'array'),
    change_note text NOT NULL DEFAULT '',
    created_by uuid REFERENCES gateway.admin_accounts(id),
    created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
    UNIQUE (template_id, version)
);

ALTER TABLE gateway.prompt_templates
    ADD CONSTRAINT prompt_templates_current_version_fk
    FOREIGN KEY (id, current_version)
    REFERENCES gateway.prompt_versions(template_id, version)
    DEFERRABLE INITIALLY DEFERRED;

CREATE INDEX IF NOT EXISTS prompt_templates_visible_idx
    ON gateway.prompt_templates (enabled, updated_at DESC);

CREATE TABLE IF NOT EXISTS gateway.knowledge_bases (
    id uuid PRIMARY KEY,
    name text NOT NULL UNIQUE,
    description text NOT NULL DEFAULT '',
    retrieval_mode text NOT NULL DEFAULT 'postgres_fts'
        CHECK (retrieval_mode IN ('postgres_fts')),
    chunk_size integer NOT NULL DEFAULT 800 CHECK (chunk_size BETWEEN 200 AND 8000),
    chunk_overlap integer NOT NULL DEFAULT 100 CHECK (chunk_overlap >= 0 AND chunk_overlap <= chunk_size / 2),
    department_ids uuid[] NOT NULL DEFAULT '{}',
    enabled boolean NOT NULL DEFAULT true,
    revision bigint NOT NULL DEFAULT 1,
    created_by uuid REFERENCES gateway.admin_accounts(id),
    created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
    updated_at timestamptz NOT NULL DEFAULT clock_timestamp()
);

CREATE TABLE IF NOT EXISTS gateway.knowledge_documents (
    id uuid PRIMARY KEY,
    knowledge_base_id uuid NOT NULL REFERENCES gateway.knowledge_bases(id) ON DELETE CASCADE,
    title text NOT NULL,
    source_type text NOT NULL DEFAULT 'text' CHECK (source_type IN ('text', 'url', 'import')),
    source_uri text NOT NULL DEFAULT '',
    content text NOT NULL CHECK (octet_length(content) <= 2097152),
    content_sha256 char(64) NOT NULL,
    status text NOT NULL DEFAULT 'ready' CHECK (status IN ('ready', 'failed')),
    status_message text NOT NULL DEFAULT '',
    chunk_count integer NOT NULL DEFAULT 0 CHECK (chunk_count >= 0),
    created_by uuid REFERENCES gateway.admin_accounts(id),
    created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
    updated_at timestamptz NOT NULL DEFAULT clock_timestamp(),
    UNIQUE (knowledge_base_id, content_sha256)
);

CREATE TABLE IF NOT EXISTS gateway.knowledge_chunks (
    id uuid PRIMARY KEY,
    knowledge_base_id uuid NOT NULL REFERENCES gateway.knowledge_bases(id) ON DELETE CASCADE,
    document_id uuid NOT NULL REFERENCES gateway.knowledge_documents(id) ON DELETE CASCADE,
    chunk_index integer NOT NULL CHECK (chunk_index >= 0),
    content text NOT NULL,
    search_vector tsvector GENERATED ALWAYS AS (to_tsvector('simple', content)) STORED,
    created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
    UNIQUE (document_id, chunk_index)
);

CREATE INDEX IF NOT EXISTS knowledge_chunks_search_idx
    ON gateway.knowledge_chunks USING gin (search_vector);
CREATE INDEX IF NOT EXISTS knowledge_chunks_base_idx
    ON gateway.knowledge_chunks (knowledge_base_id, document_id, chunk_index);

CREATE TABLE IF NOT EXISTS gateway.tool_definitions (
    id uuid PRIMARY KEY,
    code text NOT NULL UNIQUE CHECK (code ~ '^[a-z][a-z0-9_-]{1,63}$'),
    name text NOT NULL,
    description text NOT NULL DEFAULT '',
    endpoint_url text NOT NULL,
    http_method text NOT NULL DEFAULT 'POST' CHECK (http_method IN ('GET','POST','PUT','PATCH','DELETE')),
    encrypted_headers bytea NOT NULL DEFAULT ''::bytea,
    headers_kek_version integer NOT NULL DEFAULT 1,
    input_schema jsonb NOT NULL DEFAULT '{}'::jsonb CHECK (jsonb_typeof(input_schema) = 'object'),
    timeout_seconds integer NOT NULL DEFAULT 15 CHECK (timeout_seconds BETWEEN 1 AND 120),
    department_ids uuid[] NOT NULL DEFAULT '{}',
    enabled boolean NOT NULL DEFAULT true,
    revision bigint NOT NULL DEFAULT 1,
    created_by uuid REFERENCES gateway.admin_accounts(id),
    created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
    updated_at timestamptz NOT NULL DEFAULT clock_timestamp()
);

CREATE TABLE IF NOT EXISTS gateway.applications (
    id uuid PRIMARY KEY,
    code text NOT NULL UNIQUE CHECK (code ~ '^[a-z][a-z0-9_-]{2,63}$'),
    name text NOT NULL,
    description text NOT NULL DEFAULT '',
    department_ids uuid[] NOT NULL DEFAULT '{}',
    status text NOT NULL DEFAULT 'draft' CHECK (status IN ('draft','active','suspended','retired')),
    draft_config jsonb NOT NULL DEFAULT '{}'::jsonb CHECK (jsonb_typeof(draft_config) = 'object'),
    published_version integer CHECK (published_version > 0),
    revision bigint NOT NULL DEFAULT 1,
    created_by uuid REFERENCES gateway.admin_accounts(id),
    created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
    updated_at timestamptz NOT NULL DEFAULT clock_timestamp()
);

CREATE TABLE IF NOT EXISTS gateway.tool_runs (
    id uuid PRIMARY KEY,
    tool_id uuid NOT NULL REFERENCES gateway.tool_definitions(id) ON DELETE CASCADE,
    api_key_id uuid REFERENCES gateway.api_keys(id) ON DELETE SET NULL,
    request_id text NOT NULL,
    status text NOT NULL CHECK (status IN ('success','error')),
    response_status integer,
    latency_ms bigint NOT NULL DEFAULT 0,
    error text NOT NULL DEFAULT '',
    created_at timestamptz NOT NULL DEFAULT clock_timestamp()
);

CREATE INDEX IF NOT EXISTS tool_runs_tool_time_idx
    ON gateway.tool_runs (tool_id, created_at DESC);

CREATE TABLE IF NOT EXISTS gateway.application_versions (
    id uuid PRIMARY KEY,
    application_id uuid NOT NULL REFERENCES gateway.applications(id) ON DELETE CASCADE,
    version integer NOT NULL CHECK (version > 0),
    config jsonb NOT NULL CHECK (jsonb_typeof(config) = 'object'),
    change_note text NOT NULL DEFAULT '',
    published_by uuid REFERENCES gateway.admin_accounts(id),
    created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
    UNIQUE (application_id, version)
);

ALTER TABLE gateway.applications
    ADD CONSTRAINT applications_published_version_fk
    FOREIGN KEY (id, published_version)
    REFERENCES gateway.application_versions(application_id, version)
    DEFERRABLE INITIALLY DEFERRED;

CREATE TABLE IF NOT EXISTS gateway.application_runs (
    id uuid PRIMARY KEY,
    application_id uuid NOT NULL REFERENCES gateway.applications(id) ON DELETE CASCADE,
    version integer NOT NULL,
    api_key_id uuid REFERENCES gateway.api_keys(id) ON DELETE SET NULL,
    request_id text NOT NULL,
    status text NOT NULL CHECK (status IN ('success','error')),
    latency_ms bigint NOT NULL DEFAULT 0,
    retrieval_count integer NOT NULL DEFAULT 0,
    tool_count integer NOT NULL DEFAULT 0,
    error text NOT NULL DEFAULT '',
    created_at timestamptz NOT NULL DEFAULT clock_timestamp()
);

CREATE INDEX IF NOT EXISTS application_runs_app_time_idx
    ON gateway.application_runs (application_id, created_at DESC);

CREATE TABLE IF NOT EXISTS gateway.notification_channels (
    id uuid PRIMARY KEY,
    name text NOT NULL UNIQUE,
    webhook_url text NOT NULL,
    encrypted_signing_secret bytea NOT NULL DEFAULT ''::bytea,
    signing_secret_kek_version integer NOT NULL DEFAULT 1,
    event_patterns text[] NOT NULL DEFAULT '{}',
    enabled boolean NOT NULL DEFAULT true,
    revision bigint NOT NULL DEFAULT 1,
    created_by uuid REFERENCES gateway.admin_accounts(id),
    created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
    updated_at timestamptz NOT NULL DEFAULT clock_timestamp()
);

CREATE TABLE IF NOT EXISTS gateway.notification_deliveries (
    id uuid PRIMARY KEY,
    channel_id uuid NOT NULL REFERENCES gateway.notification_channels(id) ON DELETE CASCADE,
    event_id uuid NOT NULL,
    event_type text NOT NULL,
    payload jsonb NOT NULL DEFAULT '{}'::jsonb,
    status text NOT NULL CHECK (status IN ('pending','delivered','failed')),
    attempts integer NOT NULL DEFAULT 0,
    response_status integer,
    last_error text NOT NULL DEFAULT '',
    delivered_at timestamptz,
    created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
    updated_at timestamptz NOT NULL DEFAULT clock_timestamp(),
    UNIQUE (channel_id, event_id)
);

CREATE INDEX IF NOT EXISTS notification_deliveries_status_idx
    ON gateway.notification_deliveries (status, updated_at DESC);

COMMENT ON TABLE gateway.knowledge_documents IS
    'Baseline stores bounded extracted text in PostgreSQL; blob/object storage is deliberately not required.';
COMMENT ON TABLE gateway.application_versions IS
    'Immutable published application compositions referencing prompt, knowledge and tool assets.';
