create type phtx.image_data_image_type as enum ('full', 'preview');
create type phtx.image_data_storage_type as enum ('local');
create type phtx.image_data_file_format as enum ('jpeg', 'jxl', 'x-canon-cr2');
create type phtx.image_field_type as enum ('text', 'integer', 'float', 'enumerated');
create type phtx.token_invalidation_reason as enum ('user', 'reuse_attempt');
create type phtx.token_scope as enum (
  -- Unsure if we want to do more than this later
  'photos:read',
  'photos:write',
  'photos:upload',
  'photos:delete',
  'photos:share',
  'albums:read',
  'albums:write',
  'albums:delete',
  'albums:share',
  'user:profile:read',
  'user:profile:write',
  'user:account:read',
  'user:account:write',
  'user:account:delete',
  'user:rules:read',
  'user:rules:write',
  'user:rules:delete',
  'admin:users:read',
  'admin:users:write',
  'admin:users:delete',
  'auth:clients:read',
  'auth:clients:register',
  'auth:clients:remove',
  'auth:refresh'
);

create table phtx."user" (
  user_uuid uuid primary key default phtx_ext.uuid_generate_v4(),
  user_email phtx_ext.citext unique not null,
  user_username phtx_ext.citext unique not null,
  user_display_name text,
  user_password_hash text,
  user_time_created timestamptz not null default now(),
  user_time_password_changed timestamptz,
  user_time_email_confirmed timestamptz
);

create table phtx."user_token" (
  user_token_uuid uuid primary key default phtx_ext.uuid_generate_v4(),
  user_token_hash text not null,
  user_token_issued timestamptz not null default now(),
  user_token_expires timestamptz not null default now() + '1 hour',
  user_token_invalidated timestamptz null default null,
  user_token_invalidation_reason phtx.token_invalidation_reason null default null,

  user_token_scopes phtx.token_scope[] not null default array[]::phtx.token_scope[],

  constraint user_token_invalidation_needs_reason check (
    (user_token_invalidated is null and user_token_invalidation_reason is null)
    or (user_token_invalidated is not null and user_token_invalidation_reason is not null)
  ),

  constraint refresh_token_no_other_scopes check (
    (array_position(user_token_scopes, 'auth:refresh') is null)
    or (array_length(user_token_scopes, 0) = 1)
  )
);

create table phtx."login_session" (
  login_session_uuid uuid primary key default phtx_ext.uuid_generate_v4(),
  login_session_time_created timestamptz not null default now(),
  login_session_time_last_active timestamptz not null default now(),
  login_session_duration interval not null,
  login_session_time_logged_out timestamptz null,
  login_session_ip_address varchar(50)  not null,
  login_session_user_agent varchar(500) not null,
  login_session_referer    varchar(500) not null,
  login_session_user_uuid uuid not null references phtx."user" (user_uuid)
);

create table phtx."image" (
  image_uuid uuid primary key default phtx_ext.uuid_generate_v4(),
  image_original_filename text not null,
  image_title text,
  image_description text,
  image_owner uuid not null references phtx."user" (user_uuid)
);

create table phtx."image_data" (
  image_data_uuid uuid primary key default phtx_ext.uuid_generate_v4(),
  image_data_image uuid not null references phtx."image" (image_uuid),
  image_data_storage_type phtx.image_data_storage_type not null,
  image_data_image_type phtx.image_data_image_type not null,
  image_data_dimensions_width integer not null,
  image_data_dimensions_height integer not null,
  image_data_file_format phtx.image_data_file_format not null,
  
  -- Compound key to constrain specific storage types
  unique (image_data_uuid, image_data_storage_type)
);

create unique index "only_one_full_image" on phtx."image_data" (image_data_image)
  where (image_data_image_type = 'full');

create table phtx."image_data_local" (
  image_data_uuid uuid primary key default phtx_ext.uuid_generate_v4(),
  image_data_storage_type phtx.image_data_storage_type check ( image_data_storage_type = 'local' ),

  image_data_file_path text not null,
  image_data_file_sha256 varchar(64),

  foreign key (image_data_uuid, image_data_storage_type) references phtx."image_data" (image_data_uuid, image_data_storage_type)
);

create table phtx."field" (
  field_uuid uuid primary key default phtx_ext.uuid_generate_v4(),
  field_type phtx.image_field_type not null,
  field_user uuid references phtx."user" (user_uuid),

  -- Compound key to constrain image field types
  unique (field_uuid, field_type)
);

create table phtx."image_field" (
  image_field_uuid uuid primary key default phtx_ext.uuid_generate_v4(),
  image_field_type phtx.image_field_type not null,
  image_field_image uuid not null references phtx."image" (image_uuid),
  image_field_field uuid not null references phtx."field" (field_uuid),

  -- Constrain an image-field record so its type matches the referenced field definition
  foreign key (image_field_field, image_field_type) references phtx."field" (field_uuid, field_type),

  -- Compound key to constrain value types
  unique (image_field_uuid, image_field_type)
);

create table phtx."image_field_text" (
  image_field_uuid uuid primary key default phtx_ext.uuid_generate_v4(),
  image_field_type phtx.image_field_type not null check (image_field_type = 'text'),

  image_field_value text not null,

  foreign key (image_field_uuid, image_field_type) references phtx."image_field" (image_field_uuid, image_field_type)
);

create table phtx."image_field_integer" (
  image_field_uuid uuid primary key default phtx_ext.uuid_generate_v4(),
  image_field_type phtx.image_field_type not null check (image_field_type = 'integer'),

  image_field_value bigint not null,

  foreign key (image_field_uuid, image_field_type) references phtx."image_field" (image_field_uuid, image_field_type)
);

create table phtx."image_field_float" (
  image_field_uuid uuid primary key default phtx_ext.uuid_generate_v4(),
  image_field_type phtx.image_field_type not null check (image_field_type = 'float'),

  image_field_value double precision not null,

  foreign key (image_field_uuid, image_field_type) references phtx."image_field" (image_field_uuid, image_field_type)
);

create table phtx."image_field_enumerated" (
  image_field_uuid uuid primary key default phtx_ext.uuid_generate_v4(),
  image_field_type phtx.image_field_type not null check (image_field_type = 'integer'),

  image_field_value text not null,

  foreign key (image_field_uuid, image_field_type) references phtx."image_field" (image_field_uuid, image_field_type)
);
