/*
 * -------------------------------------------------------
 * CMS Foundation Schema
 * Phase 1: Enums, permissions, account_settings, audit_log,
 *          GDPR register, file metadata, storage buckets.
 * -------------------------------------------------------
 */

-- =====================================================
-- 1. New CMS Enums
-- =====================================================

create type public.cms_field_type as enum(
  'text',
  'textarea',
  'richtext',
  'checkbox',
  'radio',
  'hidden',
  'select',
  'password',
  'file',
  'date',
  'time',
  'decimal',
  'integer',
  'email',
  'phone',
  'url',
  'currency',
  'iban',
  'color',
  'computed'
);

create type public.cms_module_status as enum(
  'active',
  'inactive',
  'archived'
);

create type public.cms_record_status as enum(
  'active',
  'locked',
  'deleted',
  'archived'
);

create type public.gdpr_legal_basis as enum(
  'consent',
  'contract',
  'legal_obligation',
  'vital_interest',
  'public_interest',
  'legitimate_interest'
);

create type public.cms_account_type as enum(
  'verein',
  'vhs',
  'hotel',
  'kommune',
  'generic'
);

create type public.audit_action as enum(
  'insert',
  'update',
  'delete',
  'lock'
);

-- =====================================================
-- 2. Extend app_permissions with CMS values
-- =====================================================
-- ALTER TYPE … ADD VALUE cannot run inside a transaction in
-- older Postgres, but Supabase migrations run outside
-- explicit transactions by default.

alter type public.app_permissions add value if not exists 'modules.read';
alter type public.app_permissions add value if not exists 'modules.write';
alter type public.app_permissions add value if not exists 'modules.delete';
alter type public.app_permissions add value if not exists 'modules.insert';
alter type public.app_permissions add value if not exists 'modules.lock';
alter type public.app_permissions add value if not exists 'modules.import';
alter type public.app_permissions add value if not exists 'modules.export';
alter type public.app_permissions add value if not exists 'modules.print';
alter type public.app_permissions add value if not exists 'modules.manage';
alter type public.app_permissions add value if not exists 'members.read';
alter type public.app_permissions add value if not exists 'members.write';
alter type public.app_permissions add value if not exists 'courses.read';
alter type public.app_permissions add value if not exists 'courses.write';
alter type public.app_permissions add value if not exists 'bookings.read';
alter type public.app_permissions add value if not exists 'bookings.write';
alter type public.app_permissions add value if not exists 'finance.read';
alter type public.app_permissions add value if not exists 'finance.write';
alter type public.app_permissions add value if not exists 'finance.sepa';
alter type public.app_permissions add value if not exists 'documents.generate';
alter type public.app_permissions add value if not exists 'newsletter.send';

-- =====================================================
-- 3. account_settings — extends accounts per tenant
-- =====================================================
create table if not exists public.account_settings (
  account_id uuid primary key references public.accounts(id) on delete cascade,

  -- Organisation type
  account_type public.cms_account_type not null default 'generic',

  -- Org identity
  org_name text,
  org_address text,
  org_postal_code text,
  org_city text,
  org_phone text,
  org_email text,
  org_website text,
  org_chairman text,

  -- Branding
  logo_url text,
  primary_color text default '#0f172a',
  secondary_color text default '#3b82f6',

  -- SEPA banking
  creditor_id text,        -- Gläubiger-ID
  iban text,
  bic text,

  -- Email
  email_sender_name text,
  email_footer text,

  -- Feature flags per tenant (jsonb for flexibility)
  features jsonb not null default '{}'::jsonb,

  created_at timestamptz not null default now(),
  updated_at timestamptz not null default now()
);

comment on table public.account_settings is 'CMS-specific settings per team account (organisation, branding, SEPA, features)';

-- RLS
alter table public.account_settings enable row level security;

-- Revoke + grant
revoke all on public.account_settings from authenticated, service_role;
grant select, insert, update on public.account_settings to authenticated;
grant all on public.account_settings to service_role;

-- SELECT: any member of the account
create policy account_settings_select
  on public.account_settings for select
  to authenticated
  using (public.has_role_on_account(account_id));

-- INSERT: settings.manage permission
create policy account_settings_insert
  on public.account_settings for insert
  to authenticated
  with check (
    public.has_permission(auth.uid(), account_id, 'settings.manage'::public.app_permissions)
  );

-- UPDATE: settings.manage permission
create policy account_settings_update
  on public.account_settings for update
  to authenticated
  using (
    public.has_permission(auth.uid(), account_id, 'settings.manage'::public.app_permissions)
  );

-- Auto-update updated_at
create or replace function public.update_account_settings_timestamp()
returns trigger
language plpgsql
security definer
set search_path = ''
as $$
begin
  new.updated_at = now();
  return new;
end;
$$;

create trigger trg_account_settings_updated_at
  before update on public.account_settings
  for each row
  execute function public.update_account_settings_timestamp();

-- =====================================================
-- 4. audit_log — immutable action log
-- =====================================================
create table if not exists public.audit_log (
  id bigint generated always as identity primary key,
  account_id uuid not null references public.accounts(id) on delete cascade,
  user_id uuid references auth.users(id) on delete set null,
  table_name text not null,
  record_id text not null,
  action public.audit_action not null,
  old_data jsonb,
  new_data jsonb,
  ip_address inet,
  user_agent text,
  created_at timestamptz not null default now()
);

comment on table public.audit_log is 'Immutable audit trail for CMS data changes';

-- Indexes
create index ix_audit_log_account on public.audit_log(account_id);
create index ix_audit_log_table_record on public.audit_log(table_name, record_id);
create index ix_audit_log_created on public.audit_log(created_at desc);
create index ix_audit_log_user on public.audit_log(user_id);

-- RLS
alter table public.audit_log enable row level security;

revoke all on public.audit_log from authenticated, service_role;
grant select, insert on public.audit_log to authenticated;
grant all on public.audit_log to service_role;

-- SELECT: members of the account
create policy audit_log_select
  on public.audit_log for select
  to authenticated
  using (public.has_role_on_account(account_id));

-- INSERT: any authenticated member of the account
create policy audit_log_insert
  on public.audit_log for insert
  to authenticated
  with check (public.has_role_on_account(account_id));

-- No UPDATE/DELETE — audit log is append-only

-- =====================================================
-- 5. gdpr_processing_register
-- =====================================================
create table if not exists public.gdpr_processing_register (
  id bigint generated always as identity primary key,
  account_id uuid not null references public.accounts(id) on delete cascade,
  purpose text not null,
  legal_basis public.gdpr_legal_basis not null,
  data_categories text not null,
  data_subjects text not null,
  recipients text,
  retention_period text not null,
  technical_measures text,
  responsible_person text,
  notes text,
  created_at timestamptz not null default now(),
  updated_at timestamptz not null default now()
);

comment on table public.gdpr_processing_register is 'GDPR Art. 30 processing activity register per account';

-- Index
create index ix_gdpr_register_account on public.gdpr_processing_register(account_id);

-- RLS
alter table public.gdpr_processing_register enable row level security;

revoke all on public.gdpr_processing_register from authenticated, service_role;
grant select, insert, update, delete on public.gdpr_processing_register to authenticated;
grant all on public.gdpr_processing_register to service_role;

create policy gdpr_register_select
  on public.gdpr_processing_register for select
  to authenticated
  using (public.has_role_on_account(account_id));

create policy gdpr_register_insert
  on public.gdpr_processing_register for insert
  to authenticated
  with check (
    public.has_permission(auth.uid(), account_id, 'settings.manage'::public.app_permissions)
  );

create policy gdpr_register_update
  on public.gdpr_processing_register for update
  to authenticated
  using (
    public.has_permission(auth.uid(), account_id, 'settings.manage'::public.app_permissions)
  );

create policy gdpr_register_delete
  on public.gdpr_processing_register for delete
  to authenticated
  using (
    public.has_permission(auth.uid(), account_id, 'settings.manage'::public.app_permissions)
  );

-- Auto-update updated_at
create trigger trg_gdpr_register_updated_at
  before update on public.gdpr_processing_register
  for each row
  execute function public.update_account_settings_timestamp();

-- =====================================================
-- 6. cms_files — file metadata with Storage paths
-- =====================================================
create table if not exists public.cms_files (
  id bigint generated always as identity primary key,
  account_id uuid not null references public.accounts(id) on delete cascade,
  module_name text not null,
  record_id text not null,
  field_name text not null,
  file_name text not null,
  original_name text not null,
  mime_type text not null,
  file_size bigint not null default 0,
  storage_path text not null,
  created_at timestamptz not null default now(),
  created_by uuid references auth.users(id) on delete set null
);

comment on table public.cms_files is 'File metadata linking CMS records to Supabase Storage objects';

-- Indexes
create index ix_cms_files_account on public.cms_files(account_id);
create index ix_cms_files_record on public.cms_files(module_name, record_id);
create index ix_cms_files_field on public.cms_files(module_name, record_id, field_name);

-- RLS
alter table public.cms_files enable row level security;

revoke all on public.cms_files from authenticated, service_role;
grant select, insert, update, delete on public.cms_files to authenticated;
grant all on public.cms_files to service_role;

create policy cms_files_select
  on public.cms_files for select
  to authenticated
  using (public.has_role_on_account(account_id));

create policy cms_files_insert
  on public.cms_files for insert
  to authenticated
  with check (public.has_role_on_account(account_id));

create policy cms_files_update
  on public.cms_files for update
  to authenticated
  using (public.has_role_on_account(account_id));

create policy cms_files_delete
  on public.cms_files for delete
  to authenticated
  using (public.has_role_on_account(account_id));

-- =====================================================
-- 7. Storage Buckets
-- =====================================================

-- CMS uploads (module file fields)
insert into storage.buckets (id, name, public)
values ('cms-uploads', 'cms-uploads', false)
on conflict (id) do nothing;

-- CMS documents (generated PDFs, invoices)
insert into storage.buckets (id, name, public)
values ('cms-documents', 'cms-documents', false)
on conflict (id) do nothing;

-- CMS exports (CSV, Excel exports)
insert into storage.buckets (id, name, public)
values ('cms-exports', 'cms-exports', false)
on conflict (id) do nothing;

-- Storage RLS: account members can access their own files
-- Uses the first path segment as account_id
create or replace function kit.get_storage_path_account_id(name text)
returns uuid
language plpgsql
security definer
set search_path = ''
as $$
begin
  -- Path format: {account_id}/...
  return split_part(name, '/', 1)::uuid;
exception when others then
  return null;
end;
$$;

grant execute on function kit.get_storage_path_account_id(text)
  to authenticated, service_role;

-- RLS for cms-uploads bucket
create policy cms_uploads_select
  on storage.objects for select
  to authenticated
  using (
    bucket_id = 'cms-uploads'
    and public.has_role_on_account(kit.get_storage_path_account_id(name))
  );

create policy cms_uploads_insert
  on storage.objects for insert
  to authenticated
  with check (
    bucket_id = 'cms-uploads'
    and public.has_role_on_account(kit.get_storage_path_account_id(name))
  );

create policy cms_uploads_delete
  on storage.objects for delete
  to authenticated
  using (
    bucket_id = 'cms-uploads'
    and public.has_role_on_account(kit.get_storage_path_account_id(name))
  );

-- RLS for cms-documents bucket
create policy cms_documents_select
  on storage.objects for select
  to authenticated
  using (
    bucket_id = 'cms-documents'
    and public.has_role_on_account(kit.get_storage_path_account_id(name))
  );

create policy cms_documents_insert
  on storage.objects for insert
  to authenticated
  with check (
    bucket_id = 'cms-documents'
    and public.has_role_on_account(kit.get_storage_path_account_id(name))
  );

-- RLS for cms-exports bucket
create policy cms_exports_select
  on storage.objects for select
  to authenticated
  using (
    bucket_id = 'cms-exports'
    and public.has_role_on_account(kit.get_storage_path_account_id(name))
  );

create policy cms_exports_insert
  on storage.objects for insert
  to authenticated
  with check (
    bucket_id = 'cms-exports'
    and public.has_role_on_account(kit.get_storage_path_account_id(name))
  );

