-- M9: bounded observability for application and digital-employee runs.
-- Trace rows intentionally store metadata only; prompts, tool arguments and
-- model responses remain outside this table.

CREATE TABLE IF NOT EXISTS gateway.agent_traces (
    id uuid PRIMARY KEY,
    request_id text NOT NULL,
    api_key_id uuid REFERENCES gateway.api_keys(id) ON DELETE SET NULL,
    tenant_id uuid,
    trace_type text NOT NULL CHECK (trace_type IN ('application', 'digital_employee')),
    target_id uuid,
    target_code text NOT NULL CHECK (length(target_code) BETWEEN 1 AND 128),
    conversation_id text NOT NULL DEFAULT '' CHECK (length(conversation_id) <= 128),
    status text NOT NULL DEFAULT 'running' CHECK (status IN ('running', 'success', 'error')),
    started_at timestamptz NOT NULL DEFAULT clock_timestamp(),
    finished_at timestamptz,
    latency_ms integer,
    retrieval_count integer NOT NULL DEFAULT 0 CHECK (retrieval_count >= 0),
    model_call_count integer NOT NULL DEFAULT 0 CHECK (model_call_count >= 0),
    tool_call_count integer NOT NULL DEFAULT 0 CHECK (tool_call_count >= 0),
    error text NOT NULL DEFAULT '' CHECK (length(error) <= 4000),
    metadata jsonb NOT NULL DEFAULT '{}'::jsonb CHECK (jsonb_typeof(metadata) = 'object')
);

CREATE INDEX IF NOT EXISTS agent_traces_started_idx
    ON gateway.agent_traces (started_at DESC, id DESC);
CREATE INDEX IF NOT EXISTS agent_traces_request_idx
    ON gateway.agent_traces (request_id, started_at DESC);
CREATE INDEX IF NOT EXISTS agent_traces_target_idx
    ON gateway.agent_traces (trace_type, target_code, started_at DESC);
CREATE INDEX IF NOT EXISTS agent_traces_status_idx
    ON gateway.agent_traces (status, started_at DESC);

CREATE TABLE IF NOT EXISTS gateway.agent_trace_spans (
    id uuid PRIMARY KEY,
    trace_id uuid NOT NULL REFERENCES gateway.agent_traces(id) ON DELETE CASCADE,
    parent_id uuid REFERENCES gateway.agent_trace_spans(id) ON DELETE SET NULL,
    span_type text NOT NULL CHECK (span_type IN ('model', 'tool', 'retrieval')),
    name text NOT NULL CHECK (length(name) BETWEEN 1 AND 256),
    status text NOT NULL DEFAULT 'running' CHECK (status IN ('running', 'success', 'error')),
    started_at timestamptz NOT NULL DEFAULT clock_timestamp(),
    finished_at timestamptz,
    latency_ms integer,
    provider_code text,
    model text,
    input_tokens bigint NOT NULL DEFAULT 0 CHECK (input_tokens >= 0),
    output_tokens bigint NOT NULL DEFAULT 0 CHECK (output_tokens >= 0),
    round integer NOT NULL DEFAULT 0 CHECK (round >= 0),
    error text NOT NULL DEFAULT '' CHECK (length(error) <= 4000),
    metadata jsonb NOT NULL DEFAULT '{}'::jsonb CHECK (jsonb_typeof(metadata) = 'object')
);

CREATE INDEX IF NOT EXISTS agent_trace_spans_trace_idx
    ON gateway.agent_trace_spans (trace_id, started_at, id);
CREATE INDEX IF NOT EXISTS agent_trace_spans_type_idx
    ON gateway.agent_trace_spans (span_type, started_at DESC);

ALTER TABLE gateway.application_runs
    ADD COLUMN IF NOT EXISTS trace_id uuid REFERENCES gateway.agent_traces(id) ON DELETE SET NULL;
ALTER TABLE gateway.digital_employee_runs
    ADD COLUMN IF NOT EXISTS trace_id uuid REFERENCES gateway.agent_traces(id) ON DELETE SET NULL;
CREATE INDEX IF NOT EXISTS application_runs_trace_idx
    ON gateway.application_runs (trace_id) WHERE trace_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS digital_employee_runs_trace_idx
    ON gateway.digital_employee_runs (trace_id) WHERE trace_id IS NOT NULL;
