/*
 * -------------------------------------------------------
 * Account Hierarchy Support
 *
 * Adds parent_account_id to accounts for Verein/Subverein/Verband
 * hierarchy. This migration is additive — no behavior change until
 * hierarchy-aware RLS policies are added in a future migration.
 * -------------------------------------------------------
 */

-- Add nullable parent reference (team accounts only)
ALTER TABLE public.accounts
  ADD COLUMN IF NOT EXISTS parent_account_id uuid
    REFERENCES public.accounts(id) ON DELETE SET NULL;

CREATE INDEX IF NOT EXISTS ix_accounts_parent_account_id
  ON public.accounts(parent_account_id);

-- Prevent personal accounts from having a parent (idempotent)
DO $$
BEGIN
  IF NOT EXISTS (
    SELECT 1 FROM pg_constraint WHERE conname = 'accounts_personal_no_parent'
  ) THEN
    ALTER TABLE public.accounts
      ADD CONSTRAINT accounts_personal_no_parent
      CHECK (is_personal_account = false OR parent_account_id IS NULL);
  END IF;
END $$;

-- Prevent an account from being its own parent (idempotent)
DO $$
BEGIN
  IF NOT EXISTS (
    SELECT 1 FROM pg_constraint WHERE conname = 'accounts_no_self_parent'
  ) THEN
    ALTER TABLE public.accounts
      ADD CONSTRAINT accounts_no_self_parent
      CHECK (id <> parent_account_id);
  END IF;
END $$;

-- -------------------------------------------------------
-- Trigger: prevent hierarchy cycles on insert/update
-- -------------------------------------------------------
CREATE OR REPLACE FUNCTION kit.prevent_account_hierarchy_cycle()
RETURNS trigger
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = ''
AS $$
BEGIN
  IF NEW.parent_account_id IS NOT NULL THEN
    -- Walk up from the proposed parent; if we find NEW.id, it's a cycle
    IF EXISTS (
      WITH RECURSIVE ancestors AS (
        SELECT id, parent_account_id
        FROM public.accounts
        WHERE id = NEW.parent_account_id
        UNION ALL
        SELECT a.id, a.parent_account_id
        FROM public.accounts a
        JOIN ancestors anc ON a.id = anc.parent_account_id
      )
      CYCLE id SET is_cycle USING path
      SELECT 1 FROM ancestors WHERE id = NEW.id AND NOT is_cycle
    ) THEN
      RAISE EXCEPTION 'Setting parent_account_id would create a hierarchy cycle';
    END IF;
  END IF;

  RETURN NEW;
END;
$$;

CREATE OR REPLACE TRIGGER prevent_account_hierarchy_cycle
  BEFORE INSERT OR UPDATE OF parent_account_id
  ON public.accounts
  FOR EACH ROW
  WHEN (NEW.parent_account_id IS NOT NULL)
  EXECUTE FUNCTION kit.prevent_account_hierarchy_cycle();

-- -------------------------------------------------------
-- Helper: get all descendant account IDs (recursive, cycle-safe)
-- Restricted to service_role — called via RLS helper functions
-- -------------------------------------------------------
CREATE OR REPLACE FUNCTION public.get_account_descendants(root_id uuid)
RETURNS SETOF uuid
LANGUAGE sql STABLE
SET search_path = ''
AS $$
  WITH RECURSIVE tree AS (
    SELECT id, parent_account_id
    FROM public.accounts WHERE id = root_id
    UNION ALL
    SELECT a.id, a.parent_account_id
    FROM public.accounts a
    JOIN tree t ON a.parent_account_id = t.id
  )
  CYCLE id SET is_cycle USING path
  SELECT id FROM tree WHERE NOT is_cycle;
$$;

GRANT EXECUTE ON FUNCTION public.get_account_descendants(uuid)
  TO service_role;

-- -------------------------------------------------------
-- Helper: get all ancestor account IDs (walk up, cycle-safe)
-- Restricted to service_role — called via RLS helper functions
-- -------------------------------------------------------
CREATE OR REPLACE FUNCTION public.get_account_ancestors(child_id uuid)
RETURNS SETOF uuid
LANGUAGE sql STABLE
SET search_path = ''
AS $$
  WITH RECURSIVE tree AS (
    SELECT id, parent_account_id
    FROM public.accounts WHERE id = child_id
    UNION ALL
    SELECT a.id, a.parent_account_id
    FROM public.accounts a
    JOIN tree t ON a.id = t.parent_account_id
  )
  CYCLE id SET is_cycle USING path
  SELECT id FROM tree WHERE NOT is_cycle;
$$;

GRANT EXECUTE ON FUNCTION public.get_account_ancestors(uuid)
  TO service_role;

-- -------------------------------------------------------
-- Helper: get the depth of the hierarchy (0 = root, cycle-safe)
-- Restricted to service_role
-- -------------------------------------------------------
CREATE OR REPLACE FUNCTION public.get_account_depth(account_id uuid)
RETURNS integer
LANGUAGE sql STABLE
SET search_path = ''
AS $$
  WITH RECURSIVE tree AS (
    SELECT id, parent_account_id, 0 AS depth
    FROM public.accounts
    WHERE id = account_id
    UNION ALL
    SELECT a.id, a.parent_account_id, t.depth + 1
    FROM public.accounts a
    JOIN tree t ON a.id = t.parent_account_id
  )
  CYCLE id SET is_cycle USING path
  SELECT MAX(depth) FROM tree WHERE NOT is_cycle;
$$;

GRANT EXECUTE ON FUNCTION public.get_account_depth(uuid)
  TO service_role;
