-- Mikobase SQLite Schema

-- ============================================================
-- records
-- ============================================================

create table records (
	record_pk text primary key default (
		lower(
			hex(randomblob(4)) || '-' ||
			hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)), 2) || '-' ||
			substr('89ab', abs(random()) % 4 + 1, 1) || substr(hex(randomblob(2)), 2) || '-' ||
			hex(randomblob(6))
		)
	)
);

create trigger records_no_update
before update on records
begin
	select raise(fail, 'records rows are immutable');
end;

-- ============================================================
-- records_history
-- ============================================================

create table records_history (
	instance_pk    text primary key default (
		lower(
			hex(randomblob(4)) || '-' ||
			hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)), 2) || '-' ||
			substr('89ab', abs(random()) % 4 + 1, 1) || substr(hex(randomblob(2)), 2) || '-' ||
			hex(randomblob(6))
		)
	),
	record_pk  text not null references records(record_pk),
	updated_at text not null default (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
	active     integer not null default 1 check(active in (0, 1)),
	bucket     text check(active = 0 or (bucket is not null and json_type(bucket) = 'object')),
	stack      text check(active = 0 or (stack is not null and json_type(stack) = 'object')),
	whole_hash integer not null default 0 check(whole_hash in (0, 1)),
	body       text check(whole_hash = 0 or (body is not null and json_type(body) = 'object')),
	unique(record_pk, updated_at)
);

-- An instance record uses the `bucket` and `stack` columns: `bucket` is the
-- object's data hash; `stack` is the ordered hash of platters (each platter
-- being {class, warning?, bucket?}). The platter at position 1 must be named
-- "shadow" — and when no "shadow" entry is present in the stack hash, an
-- empty shadow is understood to sit at position 1.
--
-- A class-definition record (an instance of puck.uno/class) uses the
-- whole-hash form instead: the row sets whole_hash = 1 and stores the
-- definition object (containing class='puck.uno/class' plus the definition's
-- name/inherits/fields/methods properties) in `body`. The `bucket` and
-- `stack` columns are null for whole-hash rows. See the worldlets reference
-- for both shapes.

create trigger records_history_no_update
before update on records_history
begin
	select raise(fail, 'records_history rows are immutable');
end;

-- Enforce unique class names among active class-definition records.
-- A class-definition record stores its definition in `body` with
-- whole_hash = 1 and body.class = 'puck.uno/class'; the class's name
-- is body.name.
create trigger records_history_unique_class_name
before insert on records_history
when new.active = 1
	and new.whole_hash = 1
	and json_extract(new.body, '$.class') = 'puck.uno/class'
begin
	select raise(fail, 'duplicate class name')
	where exists (
		select 1
		from current_records cr
		where
			cr.record_pk != new.record_pk
			and cr.whole_hash = 1
			and json_extract(cr.body, '$.class') = 'puck.uno/class'
			and json_extract(cr.body, '$.name') = json_extract(new.body, '$.name')
	);
end;

-- ============================================================
-- current_records view
-- ============================================================

-- The latest active history row for each record.
create view current_records as
with ranked as (
	select
		rh.*,
		row_number() over (
			partition by rh.record_pk
			order by rh.updated_at desc, rh.instance_pk desc
		) as row_num
	from records_history rh
)
select
	instance_pk,
	record_pk,
	updated_at,
	active,
	bucket,
	stack,
	whole_hash,
	body
from ranked
where row_num = 1
and active = 1;

-- ============================================================
-- files
-- ============================================================

create table files (
	file_pk    text primary key default (
		lower(
			hex(randomblob(4)) || '-' ||
			hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)), 2) || '-' ||
			substr('89ab', abs(random()) % 4 + 1, 1) || substr(hex(randomblob(2)), 2) || '-' ||
			hex(randomblob(6))
		)
	),
	created_at text not null default (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
	size       integer not null,
	sha256     text not null unique
);

create trigger files_no_update
before update on files
begin
	select raise(fail, 'files rows are immutable');
end;

-- ============================================================
-- file_chunks
-- ============================================================

create table file_chunks (
	file_chunk_pk integer primary key,
	file_pk       text not null references files(file_pk) on delete cascade,
	chunk_index   integer not null check(chunk_index >= 0),
	content       blob not null,
	last          integer not null default 0 check(last in (0, 1)),
	unique(file_pk, chunk_index)
);

create unique index file_chunks_one_last_per_file
	on file_chunks(file_pk)
	where last = 1;

create trigger file_chunks_no_update
before update on file_chunks
begin
	select raise(fail, 'file_chunks rows are immutable');
end;
