-- ImageHub: content-addressed imaging dataset versioning (milestone 1 walking skeleton).
-- A dataset is owned by a user (investigator/PI). Files are stored as content-addressed,
-- globally deduped blobs in MinIO (one imagehub_blobs row per distinct sha256). The current
-- working file set lives in imagehub_dataset_files; a version freezes a manifest snapshot.
-- Admin sees all datasets (clinical data repository); owners see their own (research data).
-- Apply after 016_research_projects.sql:
--   docker exec -i initiative-postgres psql -U initiative -d initiatives < be0/migrations/017_imagehub_datasets.sql

CREATE TABLE IF NOT EXISTS imagehub_datasets (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  owner_user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
  name TEXT NOT NULL DEFAULT '',
  slug TEXT NOT NULL DEFAULT '',
  description TEXT NOT NULL DEFAULT '',
  visibility TEXT NOT NULL DEFAULT 'private' CHECK (visibility IN ('private','internal','public')),
  modality_tags JSONB NOT NULL DEFAULT '[]'::jsonb,
  default_branch TEXT NOT NULL DEFAULT 'main',
  created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
  updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_imagehub_datasets_owner ON imagehub_datasets (owner_user_id, created_at DESC);

-- Globally content-addressed blob registry: identical bytes across datasets dedupe to one row.
CREATE TABLE IF NOT EXISTS imagehub_blobs (
  sha256 TEXT PRIMARY KEY,
  size_bytes BIGINT NOT NULL DEFAULT 0,
  media_type TEXT NOT NULL DEFAULT 'application/octet-stream',
  storage_bucket TEXT NOT NULL DEFAULT '',
  storage_key TEXT NOT NULL DEFAULT '',
  created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- Current working file set on a dataset default branch (one row per logical path).
CREATE TABLE IF NOT EXISTS imagehub_dataset_files (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  dataset_id UUID NOT NULL REFERENCES imagehub_datasets(id) ON DELETE CASCADE,
  logical_path TEXT NOT NULL DEFAULT '',
  blob_sha256 TEXT NOT NULL REFERENCES imagehub_blobs(sha256) ON DELETE RESTRICT,
  size_bytes BIGINT NOT NULL DEFAULT 0,
  media_type TEXT NOT NULL DEFAULT 'application/octet-stream',
  imaging_meta JSONB NOT NULL DEFAULT '{}'::jsonb,
  uploaded_by UUID REFERENCES users(id) ON DELETE SET NULL,
  created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
  updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE UNIQUE INDEX IF NOT EXISTS uq_imagehub_dataset_files_path ON imagehub_dataset_files (dataset_id, logical_path);

-- Frozen version snapshots (the versioning spine; DAG-ready via parent_version_id).
CREATE TABLE IF NOT EXISTS imagehub_versions (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  dataset_id UUID NOT NULL REFERENCES imagehub_datasets(id) ON DELETE CASCADE,
  seq INTEGER NOT NULL DEFAULT 1,
  message TEXT NOT NULL DEFAULT '',
  manifest JSONB NOT NULL DEFAULT '[]'::jsonb,
  parent_version_id UUID REFERENCES imagehub_versions(id) ON DELETE SET NULL,
  author_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
  created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE UNIQUE INDEX IF NOT EXISTS uq_imagehub_versions_seq ON imagehub_versions (dataset_id, seq);

-- Append-only audit trail per dataset.
CREATE TABLE IF NOT EXISTS imagehub_dataset_audit (
  id BIGSERIAL PRIMARY KEY,
  dataset_id UUID NOT NULL REFERENCES imagehub_datasets(id) ON DELETE CASCADE,
  occurred_at TIMESTAMPTZ NOT NULL DEFAULT now(),
  actor_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
  actor_name TEXT NOT NULL DEFAULT '',
  role_label TEXT NOT NULL DEFAULT '',
  action TEXT NOT NULL,
  subject TEXT NOT NULL DEFAULT '',
  detail TEXT NOT NULL DEFAULT ''
);
CREATE INDEX IF NOT EXISTS idx_imagehub_dataset_audit_dataset ON imagehub_dataset_audit (dataset_id, occurred_at DESC);

COMMENT ON TABLE imagehub_datasets IS
  'ImageHub content-addressed imaging datasets. Owner and admin authz. Files dedupe into imagehub_blobs by sha256 — imagehub_versions freezes a manifest snapshot.';
