/*
 * -------------------------------------------------------
 * Member Transfer Between Accounts
 *
 * Enables transferring a member from one Verein to another
 * within the same Verband hierarchy.
 *
 * Design:
 *   - Personal data always moves with the member
 *   - Course enrollments, event registrations, bookings are
 *     linked via member_id (not account_id) so they survive
 *     the transfer automatically
 *   - Only org-specific admin data is cleared:
 *     dues_category_id (org-specific pricing),
 *     member_number (unique per org)
 *   - SEPA bank data (IBAN/BIC) is preserved, but mandate
 *     status resets to 'pending' (needs re-confirmation)
 *   - Financial records (invoices, SEPA batches) stay in
 *     source org — they're legally tied to that entity
 * -------------------------------------------------------
 */

-- Transfer log table
CREATE TABLE IF NOT EXISTS public.member_transfers (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  member_id uuid NOT NULL REFERENCES public.members(id) ON DELETE CASCADE,
  source_account_id uuid NOT NULL REFERENCES public.accounts(id) ON DELETE SET NULL,
  target_account_id uuid NOT NULL REFERENCES public.accounts(id) ON DELETE SET NULL,
  transferred_by uuid NOT NULL REFERENCES auth.users(id) ON DELETE SET NULL,
  reason text,
  -- Snapshot what was cleared so it can be reviewed later
  cleared_data jsonb NOT NULL DEFAULT '{}'::jsonb,
  transferred_at timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS ix_member_transfers_member
  ON public.member_transfers(member_id);

CREATE INDEX IF NOT EXISTS ix_member_transfers_source
  ON public.member_transfers(source_account_id);

CREATE INDEX IF NOT EXISTS ix_member_transfers_target
  ON public.member_transfers(target_account_id);

ALTER TABLE public.member_transfers ENABLE ROW LEVEL SECURITY;

REVOKE ALL ON public.member_transfers FROM authenticated, service_role;
GRANT SELECT, INSERT ON public.member_transfers TO authenticated;
GRANT ALL ON public.member_transfers TO service_role;

-- Readable by members of source or target account (or ancestor via hierarchy)
CREATE POLICY member_transfers_read ON public.member_transfers
  FOR SELECT TO authenticated
  USING (
    public.has_role_on_account_or_ancestor(source_account_id) OR
    public.has_role_on_account_or_ancestor(target_account_id)
  );

-- -------------------------------------------------------
-- Transfer function
--
-- Only clears org-specific admin data. All cross-org
-- relationships (courses, events, bookings) survive because
-- they reference member_id, not account_id.
-- -------------------------------------------------------
CREATE OR REPLACE FUNCTION public.transfer_member(
  p_member_id uuid,
  p_target_account_id uuid,
  p_reason text DEFAULT NULL,
  p_keep_sepa boolean DEFAULT true
)
RETURNS uuid -- returns the transfer log ID
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = ''
AS $$
DECLARE
  v_source_account_id uuid;
  v_transfer_id uuid;
  v_caller_id uuid;
  v_old_member_number text;
  v_old_dues_category_id uuid;
  v_old_sepa_mandate_id text;
  v_source_name text;
  v_target_name text;
  v_active_courses int;
  v_active_events int;
BEGIN
  v_caller_id := (SELECT auth.uid());

  -- Get the member's current account and data we'll clear
  SELECT account_id, member_number, dues_category_id, sepa_mandate_id
  INTO v_source_account_id, v_old_member_number, v_old_dues_category_id, v_old_sepa_mandate_id
  FROM public.members
  WHERE id = p_member_id;

  IF v_source_account_id IS NULL THEN
    RAISE EXCEPTION 'Mitglied nicht gefunden';
  END IF;

  IF v_source_account_id = p_target_account_id THEN
    RAISE EXCEPTION 'Mitglied ist bereits in dieser Organisation';
  END IF;

  -- Target must be a team account
  IF NOT EXISTS (
    SELECT 1 FROM public.accounts
    WHERE id = p_target_account_id AND is_personal_account = false
  ) THEN
    RAISE EXCEPTION 'Zielorganisation nicht gefunden';
  END IF;

  -- Verify caller has visibility on BOTH accounts via hierarchy
  IF NOT (
    public.has_role_on_account_or_ancestor(v_source_account_id)
    AND public.has_role_on_account_or_ancestor(p_target_account_id)
  ) THEN
    RAISE EXCEPTION 'Keine Berechtigung für den Transfer';
  END IF;

  -- Verify both accounts share a common ancestor (same Verband)
  IF NOT EXISTS (
    SELECT 1
    FROM public.get_account_ancestors(v_source_account_id) sa
    JOIN public.get_account_ancestors(p_target_account_id) ta ON sa = ta
  ) THEN
    RAISE EXCEPTION 'Organisationen gehören nicht zum selben Verband';
  END IF;

  -- Get org names for the transfer note
  SELECT name INTO v_source_name FROM public.accounts WHERE id = v_source_account_id;
  SELECT name INTO v_target_name FROM public.accounts WHERE id = p_target_account_id;

  -- Count active relationships (informational, for the log)
  SELECT count(*) INTO v_active_courses
  FROM public.course_participants cp
  JOIN public.courses c ON c.id = cp.course_id
  WHERE cp.member_id = p_member_id AND cp.status = 'enrolled';

  SELECT count(*) INTO v_active_events
  FROM public.event_registrations er
  JOIN public.events e ON e.id = er.event_id
  WHERE er.email = (SELECT email FROM public.members WHERE id = p_member_id)
    AND er.status IN ('confirmed', 'pending')
    AND e.event_date >= current_date;

  -- Perform the transfer
  UPDATE public.members
  SET
    account_id = p_target_account_id,
    -- Clear org-specific admin data
    dues_category_id = NULL,
    member_number = NULL,
    -- SEPA: keep bank data (IBAN/BIC/account_holder), just reset mandate status
    sepa_mandate_id = CASE WHEN p_keep_sepa THEN sepa_mandate_id ELSE NULL END,
    sepa_mandate_date = CASE WHEN p_keep_sepa THEN sepa_mandate_date ELSE NULL END,
    sepa_mandate_status = 'pending', -- always needs re-confirmation in new org
    -- Append transfer note
    notes = COALESCE(notes, '') ||
      E'\n[Transfer ' || now()::date || '] ' ||
      v_source_name || ' → ' || v_target_name ||
      COALESCE(E' — ' || p_reason, '') ||
      CASE WHEN v_active_courses > 0
        THEN E'\n  ↳ ' || v_active_courses || ' aktive Kurseinschreibungen bleiben erhalten'
        ELSE '' END ||
      CASE WHEN v_active_events > 0
        THEN E'\n  ↳ ' || v_active_events || ' aktive Veranstaltungsanmeldungen bleiben erhalten'
        ELSE '' END,
    updated_by = v_caller_id,
    updated_at = now()
  WHERE id = p_member_id;

  -- Log the transfer with snapshot of cleared data
  INSERT INTO public.member_transfers (
    member_id, source_account_id, target_account_id, transferred_by, reason, cleared_data
  ) VALUES (
    p_member_id,
    v_source_account_id,
    p_target_account_id,
    v_caller_id,
    p_reason,
    jsonb_build_object(
      'old_member_number', v_old_member_number,
      'old_dues_category_id', v_old_dues_category_id,
      'old_sepa_mandate_id', v_old_sepa_mandate_id,
      'active_courses_at_transfer', v_active_courses,
      'active_events_at_transfer', v_active_events,
      'sepa_kept', p_keep_sepa
    )
  )
  RETURNING id INTO v_transfer_id;

  RETURN v_transfer_id;
END;
$$;

GRANT EXECUTE ON FUNCTION public.transfer_member(uuid, uuid, text, boolean)
  TO authenticated, service_role;
