CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "pg_trgm";

CREATE TABLE IF NOT EXISTS sessions (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    tenant_id VARCHAR(64) DEFAULT 'default',
    created_at TIMESTAMPTZ DEFAULT NOW(),
    last_active_at TIMESTAMPTZ DEFAULT NOW(),
    expires_at TIMESTAMPTZ DEFAULT NOW() + INTERVAL '7 days',
    budget_usd NUMERIC(10, 6) DEFAULT 10.0,
    spent_usd NUMERIC(10, 6) DEFAULT 0.0,
    status VARCHAR(32) DEFAULT 'active',
    metadata JSONB DEFAULT '{}',
    context_json JSONB DEFAULT '[]',
    model_name VARCHAR(128),
    reasoning_effort VARCHAR(32),
    max_iterations INTEGER DEFAULT 300,
    yolo_mode BOOLEAN DEFAULT FALSE,
    CONSTRAINT positive_budget CHECK (budget_usd >= 0),
    CONSTRAINT positive_spent CHECK (spent_usd >= 0)
);

CREATE TABLE IF NOT EXISTS requests (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    session_id UUID REFERENCES sessions(id) ON DELETE CASCADE,
    tenant_id VARCHAR(64) DEFAULT 'default',
    trace_id VARCHAR(128),
    model VARCHAR(128) NOT NULL,
    provider VARCHAR(64) NOT NULL,
    operation_type VARCHAR(32) DEFAULT 'chat',
    input_tokens INTEGER DEFAULT 0,
    output_tokens INTEGER DEFAULT 0,
    total_tokens INTEGER GENERATED ALWAYS AS (input_tokens + output_tokens) STORED,
    cost_usd NUMERIC(10, 6) DEFAULT 0.0,
    cached BOOLEAN DEFAULT FALSE,
    latency_ms INTEGER,
    queue_time_ms INTEGER DEFAULT 0,
    request_payload JSONB,
    response_payload JSONB,
    tool_calls JSONB DEFAULT '[]',
    finish_reason VARCHAR(32),
    rate_limited BOOLEAN DEFAULT FALSE,
    circuit_open BOOLEAN DEFAULT FALSE,
    retry_count INTEGER DEFAULT 0,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    CONSTRAINT non_negative_tokens CHECK (input_tokens >= 0 AND output_tokens >= 0)
);

CREATE TABLE IF NOT EXISTS circuit_events (
    id SERIAL PRIMARY KEY,
    provider VARCHAR(64) NOT NULL,
    event_type VARCHAR(32) NOT NULL,
    failure_count INTEGER DEFAULT 0,
    details JSONB DEFAULT '{}',
    created_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE TABLE IF NOT EXISTS budget_events (
    id SERIAL PRIMARY KEY,
    session_id UUID REFERENCES sessions(id) ON DELETE CASCADE,
    event_type VARCHAR(32) NOT NULL,
    threshold_percent INTEGER,
    spent_before NUMERIC(10, 6),
    spent_after NUMERIC(10, 6),
    budget NUMERIC(10, 6),
    created_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE TABLE IF NOT EXISTS tool_executions (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    request_id UUID REFERENCES requests(id) ON DELETE CASCADE,
    tool_name VARCHAR(128) NOT NULL,
    tool_args JSONB,
    result_json JSONB,
    success BOOLEAN DEFAULT TRUE,
    error_message TEXT,
    latency_ms INTEGER,
    created_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE TABLE IF NOT EXISTS cache_events (
    id SERIAL PRIMARY KEY,
    cache_key VARCHAR(256) NOT NULL,
    cache_type VARCHAR(32) NOT NULL,
    hit BOOLEAN NOT NULL,
    ttl_seconds INTEGER,
    size_bytes INTEGER,
    created_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_sessions_tenant ON sessions(tenant_id);
CREATE INDEX IF NOT EXISTS idx_sessions_status ON sessions(status);
CREATE INDEX IF NOT EXISTS idx_sessions_expires ON sessions(expires_at);
CREATE INDEX IF NOT EXISTS idx_sessions_created ON sessions(created_at DESC);
CREATE INDEX IF NOT EXISTS idx_sessions_active ON sessions(tenant_id, status) WHERE status = 'active';
CREATE INDEX IF NOT EXISTS idx_sessions_metadata ON sessions USING GIN (metadata);

CREATE INDEX IF NOT EXISTS idx_requests_session ON requests(session_id);
CREATE INDEX IF NOT EXISTS idx_requests_tenant ON requests(tenant_id);
CREATE INDEX IF NOT EXISTS idx_requests_trace ON requests(trace_id);
CREATE INDEX IF NOT EXISTS idx_requests_provider ON requests(provider);
CREATE INDEX IF NOT EXISTS idx_requests_created ON requests(created_at DESC);
CREATE INDEX IF NOT EXISTS idx_requests_model ON requests(model);
CREATE INDEX IF NOT EXISTS idx_requests_payload ON requests USING GIN (request_payload);

CREATE INDEX IF NOT EXISTS idx_circuit_provider ON circuit_events(provider);
CREATE INDEX IF NOT EXISTS idx_circuit_created ON circuit_events(created_at DESC);
CREATE INDEX IF NOT EXISTS idx_budget_session ON budget_events(session_id);
CREATE INDEX IF NOT EXISTS idx_tool_request ON tool_executions(request_id);
CREATE INDEX IF NOT EXISTS idx_tool_name ON tool_executions(tool_name);
CREATE INDEX IF NOT EXISTS idx_cache_key ON cache_events(cache_key);
CREATE INDEX IF NOT EXISTS idx_cache_created ON cache_events(created_at DESC);

CREATE OR REPLACE FUNCTION update_last_active()
RETURNS TRIGGER AS $$
BEGIN
    NEW.last_active_at = NOW();
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS trigger_update_last_active ON sessions;
CREATE TRIGGER trigger_update_last_active
    BEFORE UPDATE ON sessions
    FOR EACH ROW
    EXECUTE FUNCTION update_last_active();

CREATE MATERIALIZED VIEW IF NOT EXISTS mv_daily_costs AS
SELECT 
    tenant_id,
    DATE(created_at) as date,
    provider,
    model,
    COUNT(*) as request_count,
    SUM(input_tokens) as total_input_tokens,
    SUM(output_tokens) as total_output_tokens,
    SUM(cost_usd) as total_cost_usd,
    AVG(latency_ms)::NUMERIC(10,2) as avg_latency_ms,
    SUM(CASE WHEN cached THEN 1 ELSE 0 END) as cache_hits,
    SUM(CASE WHEN NOT cached THEN 1 ELSE 0 END) as cache_misses
FROM requests
GROUP BY tenant_id, DATE(created_at), provider, model;

CREATE UNIQUE INDEX IF NOT EXISTS idx_mv_daily_costs ON mv_daily_costs(tenant_id, date, provider, model);

CREATE OR REPLACE FUNCTION refresh_daily_costs()
RETURNS void AS $$
BEGIN
    REFRESH MATERIALIZED VIEW CONCURRENTLY mv_daily_costs;
END;
$$ LANGUAGE plpgsql;

ALTER TABLE sessions ENABLE ROW LEVEL SECURITY;
ALTER TABLE requests ENABLE ROW LEVEL SECURITY;

CREATE POLICY tenant_isolation_sessions ON sessions
    USING (tenant_id = current_setting('app.current_tenant', true));
CREATE POLICY tenant_isolation_requests ON requests
    USING (tenant_id = current_setting('app.current_tenant', true));

GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO ml_intern;
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO ml_intern;
