-- =====================================================
-- GDPR Data Retention Automation
--
-- Configurable retention policies per account.
-- Automatic anonymization of resigned/excluded/deceased
-- members after retention period expires.
-- =====================================================

-- Retention policy configuration per account
CREATE TABLE IF NOT EXISTS public.gdpr_retention_policies (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  account_id uuid NOT NULL REFERENCES public.accounts(id) ON DELETE CASCADE,
  policy_name text NOT NULL DEFAULT 'Standard',
  retention_days int NOT NULL DEFAULT 1095, -- 3 years
  auto_anonymize boolean NOT NULL DEFAULT false,
  applies_to_status text[] NOT NULL DEFAULT ARRAY['resigned', 'excluded', 'deceased'],
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now(),
  UNIQUE(account_id)
);

ALTER TABLE public.gdpr_retention_policies ENABLE ROW LEVEL SECURITY;
REVOKE ALL ON public.gdpr_retention_policies FROM authenticated, service_role;
GRANT SELECT, INSERT, UPDATE ON public.gdpr_retention_policies TO authenticated;
GRANT ALL ON public.gdpr_retention_policies TO service_role;

CREATE POLICY gdpr_retention_select
  ON public.gdpr_retention_policies FOR SELECT TO authenticated
  USING (public.has_role_on_account(account_id));

CREATE POLICY gdpr_retention_mutate
  ON public.gdpr_retention_policies FOR ALL TO authenticated
  USING (public.has_permission(auth.uid(), account_id, 'members.write'::public.app_permissions));

-- Anonymize a single member (replaces all PII with placeholder)
CREATE OR REPLACE FUNCTION public.anonymize_member(
  p_member_id uuid,
  p_performed_by uuid DEFAULT NULL
)
RETURNS void
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = ''
AS $$
DECLARE
  v_member record;
  v_user_id uuid;
BEGIN
  v_user_id := COALESCE(p_performed_by, auth.uid());

  SELECT * INTO v_member FROM public.members WHERE id = p_member_id;
  IF v_member IS NULL THEN
    RAISE EXCEPTION 'Member not found';
  END IF;

  -- Verify caller access
  IF v_user_id IS NOT NULL AND NOT public.has_permission(v_user_id, v_member.account_id, 'members.write'::public.app_permissions) THEN
    RAISE EXCEPTION 'Access denied' USING ERRCODE = '42501';
  END IF;

  -- Snapshot full record to audit log before anonymization
  INSERT INTO public.member_audit_log (member_id, account_id, user_id, action, metadata)
  VALUES (
    p_member_id, v_member.account_id, v_user_id, 'gdpr_anonymized',
    jsonb_build_object(
      'original_first_name', v_member.first_name,
      'original_last_name', v_member.last_name,
      'original_email', v_member.email,
      'reason', 'GDPR retention policy'
    )
  );

  -- Replace all PII with anonymized placeholders
  UPDATE public.members SET
    first_name = 'ANONYMISIERT',
    last_name = 'ANONYMISIERT',
    email = NULL,
    phone = NULL,
    mobile = NULL,
    phone2 = NULL,
    fax = NULL,
    street = NULL,
    house_number = NULL,
    street2 = NULL,
    postal_code = NULL,
    city = NULL,
    date_of_birth = NULL,
    birthplace = NULL,
    birth_country = NULL,
    iban = NULL,
    bic = NULL,
    account_holder = NULL,
    sepa_mandate_reference = NULL,
    sepa_mandate_id = NULL,
    primary_mandate_id = NULL,
    guardian_name = NULL,
    guardian_phone = NULL,
    guardian_email = NULL,
    notes = '[GDPR anonymisiert am ' || to_char(now(), 'YYYY-MM-DD') || ']',
    custom_data = '{}'::jsonb,
    online_access_key = NULL,
    online_access_blocked = true,
    gdpr_consent = false,
    gdpr_newsletter = false,
    gdpr_internet = false,
    gdpr_print = false,
    gdpr_birthday_info = false,
    is_archived = true,
    updated_by = v_user_id
  WHERE id = p_member_id;

  -- Anonymize SEPA mandates (can't DELETE due to ON DELETE RESTRICT from Phase 1)
  -- primary_mandate_id already cleared above in the members UPDATE
  -- Anonymize SEPA PII fields (keep row for audit, revoke mandate)
  UPDATE public.sepa_mandates
  SET iban = 'DE00ANON0000000000000', bic = NULL, account_holder = 'ANONYMISIERT',
      mandate_reference = 'ANON-' || id::text, status = 'revoked',
      notes = '[GDPR anonymisiert]'
  WHERE member_id = p_member_id;

  -- Remove communications (may contain PII)
  BEGIN
    DELETE FROM public.member_communications WHERE member_id = p_member_id;
  EXCEPTION WHEN undefined_table THEN NULL;
  END;

  -- Remove portal invitations
  DELETE FROM public.member_portal_invitations WHERE member_id = p_member_id;
END;
$$;

GRANT EXECUTE ON FUNCTION public.anonymize_member(uuid, uuid)
  TO authenticated, service_role;

-- Batch enforcement: find and anonymize members matching retention criteria
CREATE OR REPLACE FUNCTION public.enforce_gdpr_retention_policies()
RETURNS int
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = ''
AS $$
DECLARE
  v_policy record;
  v_member record;
  v_count int := 0;
BEGIN
  FOR v_policy IN
    SELECT * FROM public.gdpr_retention_policies
    WHERE auto_anonymize = true
  LOOP
    FOR v_member IN
      SELECT m.id
      FROM public.members m
      WHERE m.account_id = v_policy.account_id
        AND m.status = ANY(v_policy.applies_to_status::public.membership_status[])
        AND m.first_name != 'ANONYMISIERT' -- not already anonymized
        AND m.exit_date IS NOT NULL -- only retain based on actual exit date
        AND m.exit_date + (v_policy.retention_days || ' days')::interval <= current_date
    LOOP
      PERFORM public.anonymize_member(v_member.id, NULL);
      v_count := v_count + 1;
    END LOOP;
  END LOOP;

  RETURN v_count;
END;
$$;

GRANT EXECUTE ON FUNCTION public.enforce_gdpr_retention_policies()
  TO service_role;
