-- ============================================================
-- clauth schema
-- Migration: 001_clauth_schema.sql
-- ============================================================

-- Enable vault extension (Supabase enables by default, but guard)
-- vault.secrets is already available in Supabase projects

-- ============================================================
-- Service Registry
-- ============================================================
create table if not exists public.clauth_services (
  id            uuid primary key default gen_random_uuid(),
  name          text not null unique,           -- e.g. 'github', 'r2'
  label         text not null,                  -- human display name
  key_type      text not null                   -- 'token' | 'keypair' | 'connstring' | 'oauth'
                check (key_type in ('token','keypair','connstring','oauth')),
  enabled       boolean not null default false,
  vault_key     text,                           -- vault secret name: 'clauth.<name>'
  description   text,
  last_retrieved timestamptz,
  last_rotated   timestamptz,
  created_at    timestamptz not null default now(),
  updated_at    timestamptz not null default now()
);

-- ============================================================
-- Machine Registry (hardware fingerprints — hashed only)
-- ============================================================
create table if not exists public.clauth_machines (
  id              uuid primary key default gen_random_uuid(),
  install_id      text not null default 'default', -- logical vault install / owner group
  machine_hash    text not null unique,          -- SHA256(machine_id + os_install_id)
  label           text,                          -- e.g. 'Dave-Desktop-Win11'
  hmac_seed_hash  text not null,                 -- SHA256 of the HMAC seed stored in vault
  enabled         boolean not null default true,
  fail_count      integer not null default 0,
  locked          boolean not null default false,
  created_at      timestamptz not null default now(),
  last_seen       timestamptz
);

-- ============================================================
-- Audit Log
-- ============================================================
create table if not exists public.clauth_audit (
  id           uuid primary key default gen_random_uuid(),
  machine_hash text,
  service_name text,
  action       text not null,    -- 'retrieve' | 'enable' | 'disable' | 'rotate' | 'revoke' | 'add' | 'remove'
  result       text not null,    -- 'success' | 'fail' | 'denied'
  detail       text,
  created_at   timestamptz not null default now()
);

-- ============================================================
-- Seed: built-in service definitions (no keys — just registry)
-- ============================================================
insert into public.clauth_services (name, label, key_type, description) values
  ('github',           'GitHub PAT',                  'token',     'GitHub Personal Access Token — repo, workflow, org'),
  ('supabase-anon',    'Supabase Anon Key',            'token',     'Supabase public anon key (RLS-gated)'),
  ('supabase-service', 'Supabase Service Role Key',    'token',     'Supabase service_role — bypasses RLS'),
  ('supabase-db',      'Supabase DB Connection',       'connstring','PostgreSQL connection string (pooled + direct)'),
  ('vercel',           'Vercel API Token',             'keypair',   'Vercel API token + Team ID'),
  ('namecheap',        'Namecheap API',                'keypair',   'Namecheap API key + username'),
  ('neo4j',            'Neo4j Aura',                   'connstring','Neo4j Aura URI + credentials'),
  ('anthropic',        'Anthropic API Key',            'token',     'Claude API — sk-ant-...'),
  ('r2',               'Cloudflare R2 Keypair',        'keypair',   'R2 Access Key ID + Secret Access Key'),
  ('r2-bucket',        'Cloudflare R2 Bucket Config',  'connstring','R2 bucket name + endpoint URL'),
  ('cloudflare',       'Cloudflare API Token',         'token',     'Cloudflare zone/DNS/admin token'),
  ('rocketreach',      'RocketReach API Key',          'token',     'RocketReach contact intelligence API')
on conflict (name) do nothing;

-- ============================================================
-- RLS — lock down all tables
-- ============================================================
alter table public.clauth_services  enable row level security;
alter table public.clauth_machines  enable row level security;
alter table public.clauth_audit     enable row level security;

-- service_role bypasses RLS — all access from Edge Function only
-- No direct anon access to any clauth table
do $$ begin
  if not exists (select 1 from pg_policies where policyname = 'no_anon_services' and tablename = 'clauth_services') then
    create policy "no_anon_services" on public.clauth_services for all using (false);
  end if;
  if not exists (select 1 from pg_policies where policyname = 'no_anon_machines' and tablename = 'clauth_machines') then
    create policy "no_anon_machines" on public.clauth_machines for all using (false);
  end if;
  if not exists (select 1 from pg_policies where policyname = 'no_anon_audit' and tablename = 'clauth_audit') then
    create policy "no_anon_audit" on public.clauth_audit for all using (false);
  end if;
end $$;

-- ============================================================
-- Updated_at trigger
-- ============================================================
create or replace function public.clauth_touch_updated()
returns trigger language plpgsql as $$
begin new.updated_at = now(); return new; end;
$$;

drop trigger if exists clauth_services_updated on public.clauth_services;
create trigger clauth_services_updated
  before update on public.clauth_services
  for each row execute procedure public.clauth_touch_updated();
